From 43110f3dd64dea519104667e03ef4022981a470c Mon Sep 17 00:00:00 2001 From: oris garno Date: Mon, 6 Jan 2025 19:27:11 +0700 Subject: [PATCH 01/11] fix(java):Compatible mode on de/serialize api failed to deserialize --- docs/guide/java_serialization_guide.md | 6 +- .../src/main/java/org/apache/fury/Fury.java | 9 +- .../org/apache/fury/resolver/ClassInfo.java | 2 +- .../compatible/CompatibleSerializerTest.java | 85 +++++++++++++++++++ .../classes/ClassCompleteField.java | 41 +++++++++ .../compatible/classes/ClassMissingField.java | 27 ++++++ .../classes/SubclassCompleteField.java | 36 ++++++++ .../classes/SubclassMissingField.java | 25 ++++++ ...aTypesTest.java => DataFieldTypeTest.java} | 2 +- 9 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java create mode 100644 java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassCompleteField.java create mode 100644 java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassMissingField.java create mode 100644 java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassCompleteField.java create mode 100644 java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassMissingField.java rename java/fury-format/src/test/java/org/apache/fury/format/type/{DataTypesTest.java => DataFieldTypeTest.java} (98%) diff --git a/docs/guide/java_serialization_guide.md b/docs/guide/java_serialization_guide.md index 5270390113..ec702bc2e3 100644 --- a/docs/guide/java_serialization_guide.md +++ b/docs/guide/java_serialization_guide.md @@ -102,7 +102,7 @@ public class Example { | `compressLong` | Enables or disables long compression for smaller size. | `true` | | `compressString` | Enables or disables string compression for smaller size. | `false` | | `classLoader` | The classloader should not be updated; Fury caches class metadata. Use `LoaderBinding` or `ThreadSafeFury` for classloader updates. | `Thread.currentThread().getContextClassLoader()` | -| `compatibleMode` | Type forward/backward compatibility config. Also Related to `checkClassVersion` config. `SCHEMA_CONSISTENT`: Class schema must be consistent between serialization peer and deserialization peer. `COMPATIBLE`: Class schema can be different between serialization peer and deserialization peer. They can add/delete fields independently. | `CompatibleMode.SCHEMA_CONSISTENT` | +| `compatibleMode` | Type forward/backward compatibility config. Also Related to `checkClassVersion` config. `SCHEMA_CONSISTENT`: Class schema must be consistent between serialization peer and deserialization peer. `COMPATIBLE`: Class schema can be different between serialization peer and deserialization peer. They can add/delete fields independently. [See more](class-inconsistency-and-class-version-check). | `CompatibleMode.SCHEMA_CONSISTENT` | | `checkClassVersion` | Determines whether to check the consistency of the class schema. If enabled, Fury checks, writes, and checks consistency using the `classVersionHash`. It will be automatically disabled when `CompatibleMode#COMPATIBLE` is enabled. Disabling is not recommended unless you can ensure the class won't evolve. | `false` | | `checkJdkClassSerializable` | Enables or disables checking of `Serializable` interface for classes under `java.*`. If a class under `java.*` is not `Serializable`, Fury will throw an `UnsupportedOperationException`. | `true` | | `registerGuavaTypes` | Whether to pre-register Guava types such as `RegularImmutableMap`/`RegularImmutableList`. These types are not public API, but seem pretty stable. | `true` | @@ -510,6 +510,10 @@ If you create fury without setting `CompatibleMode` to `org.apache.fury.config.C strange serialization error, it may be caused by class inconsistency between serialization peer and deserialization peer. +If you create fury and set `CompatibleMode` to `org.apache.fury.config.CompatibleMode.COMPATIBLE`, you need to register +the class that you want to serialize or deserialize(You don't need to register any class inside that class). +[See example here](https://github.com/apache/fury/blob/main/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java) + In such cases, you can invoke `FuryBuilder#withClassVersionCheck` to create fury to validate it, if deserialization throws `org.apache.fury.exception.ClassNotCompatibleException`, it shows class are inconsistent, and you should create fury with diff --git a/java/fury-core/src/main/java/org/apache/fury/Fury.java b/java/fury-core/src/main/java/org/apache/fury/Fury.java index bb5b102a8a..7b364042df 100644 --- a/java/fury-core/src/main/java/org/apache/fury/Fury.java +++ b/java/fury-core/src/main/java/org/apache/fury/Fury.java @@ -1070,6 +1070,7 @@ public void serializeJavaObject(MemoryBuffer buffer, Object obj) { buffer.writeInt32(-1); // preserve 4-byte for meta start offsets. if (!refResolver.writeRefOrNull(buffer, obj)) { ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass()); + classResolver.writeClass(buffer, classInfo); writeData(buffer, classInfo, obj); MetaContext metaContext = serializationContext.getMetaContext(); if (metaContext != null && !metaContext.writingClassDefs.isEmpty()) { @@ -1119,7 +1120,13 @@ public T deserializeJavaObject(MemoryBuffer buffer, Class cls) { T obj; int nextReadRefId = refResolver.tryPreserveRefId(buffer); if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - obj = (T) readDataInternal(buffer, classResolver.getClassInfo(cls)); + ClassInfo classInfo; + if (shareMeta) { + classInfo = classResolver.readClassInfo(buffer); + } else { + classInfo = classResolver.getClassInfo(cls); + } + obj = (T) readDataInternal(buffer, classInfo); return obj; } else { return null; diff --git a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java index 3ca0c85587..fb9349c68a 100644 --- a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java +++ b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java @@ -110,7 +110,7 @@ public class ClassInfo { if (cls != null) { boolean isLambda = Functions.isLambda(cls); boolean isProxy = classId != ClassResolver.REPLACE_STUB_ID && ReflectionUtils.isJdkProxy(cls); - this.isDynamicGeneratedClass = isLambda || isProxy; + this.isDynamicGeneratedClass = isLambda || isProxy || cls.getName().contains("fury.Target"); if (isLambda) { this.classId = ClassResolver.LAMBDA_STUB_ID; } diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java new file mode 100644 index 0000000000..ed78790349 --- /dev/null +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java @@ -0,0 +1,85 @@ +package org.apache.fury.serializer.compatible; + +import org.apache.fury.Fury; +import org.apache.fury.config.CompatibleMode; +import org.apache.fury.config.Language; +import org.apache.fury.serializer.compatible.classes.ClassCompleteField; +import org.apache.fury.serializer.compatible.classes.ClassMissingField; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * Test COMPATIBILITY mode that supports - same field type and name can be deserialized to other + * class with different name - scrambled field order to make sure it could handle different field + * order - missing or extra field from source class to target class - generic class + */ +public class CompatibleSerializerTest extends Assert { + + Fury getFury(Class... classes) { + Fury instance = + Fury.builder() + .withLanguage(Language.JAVA) + .withRefTracking(true) + .withCompatibleMode(CompatibleMode.COMPATIBLE) + .withMetaShare(true) + .withScopedMetaShare(true) + .requireClassRegistration(false) + .withAsyncCompilation(true) + .serializeEnumByName(true) + .build(); + if (classes != null) { + for (Class clazz : classes) { + instance.register(clazz); + } + } + ; + return instance; + } + + @Test + void testTargetHasLessFieldComparedToSourceClass() throws InterruptedException { + + ClassCompleteField subclass = new ClassCompleteField<>("subclass", "subclass2"); + ClassCompleteField> classCompleteField = + new ClassCompleteField<>(subclass, subclass); + byte[] serialized = getFury(ClassCompleteField.class).serializeJavaObject(classCompleteField); + ClassMissingField> classMissingField = + getFury(ClassMissingField.class).deserializeJavaObject(serialized, ClassMissingField.class); + + assertEq(classCompleteField, classMissingField); + } + + @Test + void testTargetHasMoreFieldComparedToSourceClass() throws InterruptedException { + + ClassMissingField subclass = new ClassMissingField<>("subclass"); + ClassMissingField classMissingField = new ClassMissingField(subclass); + byte[] serialized = getFury(ClassMissingField.class).serializeJavaObject(classMissingField); + + ClassCompleteField classCompleteField = + getFury(ClassCompleteField.class) + .deserializeJavaObject(serialized, ClassCompleteField.class); + + assertEq(classCompleteField, classMissingField); + } + + void assertEq(ClassCompleteField classCompleteField, ClassMissingField classMissingField) { + assertEqSubClass( + (ClassCompleteField) classCompleteField.getPrivateFieldSubClass(), + (ClassMissingField) classMissingField.getPrivateFieldSubClass()); + assertEquals(classCompleteField.getPrivateMap(), classMissingField.getPrivateMap()); + assertEquals(classCompleteField.getPrivateList(), classMissingField.getPrivateList()); + assertEquals(classCompleteField.getPrivateString(), classMissingField.getPrivateString()); + assertEquals(classCompleteField.getPrivateInt(), classMissingField.getPrivateInt()); + } + + void assertEqSubClass( + ClassCompleteField classCompleteField, ClassMissingField classMissingField) { + assertEquals( + classCompleteField.getPrivateFieldSubClass(), classMissingField.getPrivateFieldSubClass()); + assertEquals(classCompleteField.getPrivateMap(), classMissingField.getPrivateMap()); + assertEquals(classCompleteField.getPrivateList(), classMissingField.getPrivateList()); + assertEquals(classCompleteField.getPrivateString(), classMissingField.getPrivateString()); + assertEquals(classCompleteField.getPrivateInt(), classMissingField.getPrivateInt()); + } +} diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassCompleteField.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassCompleteField.java new file mode 100644 index 0000000000..e7e3f86019 --- /dev/null +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassCompleteField.java @@ -0,0 +1,41 @@ +package org.apache.fury.serializer.compatible.classes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@EqualsAndHashCode +@Getter +public class ClassCompleteField { + private boolean privateBoolean = true; + private int privateInt = 10; + private String privateString = "notNull"; + private Map privateMap; + private List privateList; + private T privateFieldSubClass; + + private boolean privateBoolean2 = true; + private int privateInt2 = 10; + private String privateString2 = "notNull"; + private Map privateMap2; + private List privateList2; + private T privateFieldSubClass2; + + public ClassCompleteField(T privateFieldSubClass, T privateFieldSubClass2) { + privateMap = new HashMap<>(); + privateMap.put("a", "b"); + privateList = new ArrayList<>(); + privateList.add("a"); + + privateMap2 = new HashMap<>(); + privateMap2.put("a", "b"); + privateList2 = new ArrayList<>(); + privateList2.add("a"); + + this.privateFieldSubClass = privateFieldSubClass; + this.privateFieldSubClass2 = privateFieldSubClass2; + } +} diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassMissingField.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassMissingField.java new file mode 100644 index 0000000000..ef7d7d0d1f --- /dev/null +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassMissingField.java @@ -0,0 +1,27 @@ +package org.apache.fury.serializer.compatible.classes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@EqualsAndHashCode +@Getter +public class ClassMissingField { + private T privateFieldSubClass; + private List privateList; + private Map privateMap; + private String privateString = "missing"; + private int privateInt = 999; + private boolean privateBoolean = false; + + public ClassMissingField(T privateFieldSubClass) { + privateMap = new HashMap<>(); + privateMap.put("z", "x"); + privateList = new ArrayList<>(); + privateList.add("c"); + this.privateFieldSubClass = privateFieldSubClass; + } +} diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassCompleteField.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassCompleteField.java new file mode 100644 index 0000000000..953978a340 --- /dev/null +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassCompleteField.java @@ -0,0 +1,36 @@ +package org.apache.fury.serializer.compatible.classes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode +public class SubclassCompleteField { + private boolean privateBoolean = true; + private int privateInt = 10; + private String privateString = "notNull"; + private Map privateMap; + private List privateList; + + private boolean privateBoolean2 = true; + private int privateInt2 = 10; + private String privateString2 = "notNull"; + private Map privateMap2; + private List privateList2; + + public SubclassCompleteField() { + privateMap = new HashMap<>(); + privateMap.put("a", "b"); + privateList = new ArrayList<>(); + privateList.add("a"); + + privateMap2 = new HashMap<>(); + privateMap2.put("a", "b"); + privateList2 = new ArrayList<>(); + privateList2.add("a"); + } +} diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassMissingField.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassMissingField.java new file mode 100644 index 0000000000..1c4f5651ca --- /dev/null +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassMissingField.java @@ -0,0 +1,25 @@ +package org.apache.fury.serializer.compatible.classes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode +public class SubclassMissingField { + private boolean privateBoolean = true; + private int privateInt = 10; + private String privateString = "notNull"; + private Map privateMap; + private List privateList; + + public SubclassMissingField() { + privateMap = new HashMap<>(); + privateMap.put("a", "b"); + privateList = new ArrayList<>(); + privateList.add("a"); + } +} diff --git a/java/fury-format/src/test/java/org/apache/fury/format/type/DataTypesTest.java b/java/fury-format/src/test/java/org/apache/fury/format/type/DataFieldTypeTest.java similarity index 98% rename from java/fury-format/src/test/java/org/apache/fury/format/type/DataTypesTest.java rename to java/fury-format/src/test/java/org/apache/fury/format/type/DataFieldTypeTest.java index 193bcfaae3..3c48adc1fe 100644 --- a/java/fury-format/src/test/java/org/apache/fury/format/type/DataTypesTest.java +++ b/java/fury-format/src/test/java/org/apache/fury/format/type/DataFieldTypeTest.java @@ -27,7 +27,7 @@ import org.apache.fury.test.bean.BeanA; import org.testng.annotations.Test; -public class DataTypesTest { +public class DataFieldTypeTest { @Test public void parseSchema() { From 1c7a45fee99c86911f4f752c7a0a98d5c254be93 Mon Sep 17 00:00:00 2001 From: oris garno Date: Mon, 6 Jan 2025 19:31:03 +0700 Subject: [PATCH 02/11] fix: header file --- .../compatible/CompatibleSerializerTest.java | 19 +++++++++++++++++++ .../classes/ClassCompleteField.java | 19 +++++++++++++++++++ .../compatible/classes/ClassMissingField.java | 19 +++++++++++++++++++ .../classes/SubclassCompleteField.java | 19 +++++++++++++++++++ .../classes/SubclassMissingField.java | 19 +++++++++++++++++++ 5 files changed, 95 insertions(+) diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java index ed78790349..6f4f1354f5 100644 --- a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java @@ -1,3 +1,22 @@ +/* + * 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.fury.serializer.compatible; import org.apache.fury.Fury; diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassCompleteField.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassCompleteField.java index e7e3f86019..c2b169c648 100644 --- a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassCompleteField.java +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassCompleteField.java @@ -1,3 +1,22 @@ +/* + * 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.fury.serializer.compatible.classes; import java.util.ArrayList; diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassMissingField.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassMissingField.java index ef7d7d0d1f..32e2a9e6a7 100644 --- a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassMissingField.java +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/ClassMissingField.java @@ -1,3 +1,22 @@ +/* + * 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.fury.serializer.compatible.classes; import java.util.ArrayList; diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassCompleteField.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassCompleteField.java index 953978a340..da183502d1 100644 --- a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassCompleteField.java +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassCompleteField.java @@ -1,3 +1,22 @@ +/* + * 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.fury.serializer.compatible.classes; import java.util.ArrayList; diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassMissingField.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassMissingField.java index 1c4f5651ca..233b14cb74 100644 --- a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassMissingField.java +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/classes/SubclassMissingField.java @@ -1,3 +1,22 @@ +/* + * 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.fury.serializer.compatible.classes; import java.util.ArrayList; From ead89ecf546256366dc3991950b1a1988dc774bc Mon Sep 17 00:00:00 2001 From: oris garno Date: Mon, 6 Jan 2025 23:08:50 +0700 Subject: [PATCH 03/11] fix: fix test not register classes when using COMPATIBLE mode --- .../src/main/java/org/apache/fury/resolver/ClassInfo.java | 2 +- .../java/org/apache/fury/serializer/EnumSerializerTest.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java index fb9349c68a..3ca0c85587 100644 --- a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java +++ b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java @@ -110,7 +110,7 @@ public class ClassInfo { if (cls != null) { boolean isLambda = Functions.isLambda(cls); boolean isProxy = classId != ClassResolver.REPLACE_STUB_ID && ReflectionUtils.isJdkProxy(cls); - this.isDynamicGeneratedClass = isLambda || isProxy || cls.getName().contains("fury.Target"); + this.isDynamicGeneratedClass = isLambda || isProxy; if (isLambda) { this.classId = ClassResolver.LAMBDA_STUB_ID; } diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/EnumSerializerTest.java b/java/fury-core/src/test/java/org/apache/fury/serializer/EnumSerializerTest.java index 7a5c350d49..825aa56a04 100644 --- a/java/fury-core/src/test/java/org/apache/fury/serializer/EnumSerializerTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/EnumSerializerTest.java @@ -140,8 +140,10 @@ public void testEnumSerializationAsString() { .build(); // serialize enum "B" + furySerialization.register(cls1); byte[] bytes = furySerialization.serializeJavaObject(cls1.getEnumConstants()[1]); + furyDeserialize.register(cls2); Object data = furyDeserialize.deserializeJavaObject(bytes, cls2); assertEquals(cls2.getEnumConstants()[0], data); } @@ -175,8 +177,10 @@ public void testEnumSerializationAsString_differentClass() { .build(); // serialize enum "B" + furySerialization.register(cls1); byte[] bytes = furySerialization.serializeJavaObject(cls1.getEnumConstants()[1]); + furyDeserialize.register(cls2); Object data = furyDeserialize.deserializeJavaObject(bytes, cls2); assertEquals(cls2.getEnumConstants()[0], data); } @@ -209,9 +213,11 @@ public void testEnumSerializationAsString_invalidEnum() { .withAsyncCompilation(false) .build(); + furySerialization.register(cls1); byte[] bytes = furySerialization.serializeJavaObject(cls1.getEnumConstants()[0]); try { + furyDeserialize.register(cls2); furyDeserialize.deserializeJavaObject(bytes, cls2); fail("expected to throw exception"); } catch (Exception e) { From 0423642ebac8f0c1b649c9c8d432d594bc94f7c6 Mon Sep 17 00:00:00 2001 From: oris garno Date: Mon, 6 Jan 2025 23:11:22 +0700 Subject: [PATCH 04/11] fix: revert accidental change --- .../format/type/{DataFieldTypeTest.java => DataTypeTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename java/fury-format/src/test/java/org/apache/fury/format/type/{DataFieldTypeTest.java => DataTypeTest.java} (98%) diff --git a/java/fury-format/src/test/java/org/apache/fury/format/type/DataFieldTypeTest.java b/java/fury-format/src/test/java/org/apache/fury/format/type/DataTypeTest.java similarity index 98% rename from java/fury-format/src/test/java/org/apache/fury/format/type/DataFieldTypeTest.java rename to java/fury-format/src/test/java/org/apache/fury/format/type/DataTypeTest.java index 3c48adc1fe..866637cb7b 100644 --- a/java/fury-format/src/test/java/org/apache/fury/format/type/DataFieldTypeTest.java +++ b/java/fury-format/src/test/java/org/apache/fury/format/type/DataTypeTest.java @@ -27,7 +27,7 @@ import org.apache.fury.test.bean.BeanA; import org.testng.annotations.Test; -public class DataFieldTypeTest { +public class DataTypeTest { @Test public void parseSchema() { From 3a77a280a0d2561f5f0d1029320fcf68dcfba041 Mon Sep 17 00:00:00 2001 From: orisgarno <42269241+orisgarno@users.noreply.github.com> Date: Mon, 6 Jan 2025 23:18:43 +0700 Subject: [PATCH 05/11] Update java_serialization_guide.md --- docs/guide/java_serialization_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/java_serialization_guide.md b/docs/guide/java_serialization_guide.md index ec702bc2e3..d985e59118 100644 --- a/docs/guide/java_serialization_guide.md +++ b/docs/guide/java_serialization_guide.md @@ -102,7 +102,7 @@ public class Example { | `compressLong` | Enables or disables long compression for smaller size. | `true` | | `compressString` | Enables or disables string compression for smaller size. | `false` | | `classLoader` | The classloader should not be updated; Fury caches class metadata. Use `LoaderBinding` or `ThreadSafeFury` for classloader updates. | `Thread.currentThread().getContextClassLoader()` | -| `compatibleMode` | Type forward/backward compatibility config. Also Related to `checkClassVersion` config. `SCHEMA_CONSISTENT`: Class schema must be consistent between serialization peer and deserialization peer. `COMPATIBLE`: Class schema can be different between serialization peer and deserialization peer. They can add/delete fields independently. [See more](class-inconsistency-and-class-version-check). | `CompatibleMode.SCHEMA_CONSISTENT` | +| `compatibleMode` | Type forward/backward compatibility config. Also Related to `checkClassVersion` config. `SCHEMA_CONSISTENT`: Class schema must be consistent between serialization peer and deserialization peer. `COMPATIBLE`: Class schema can be different between serialization peer and deserialization peer. They can add/delete fields independently. [See more](#class-inconsistency-and-class-version-check). | `CompatibleMode.SCHEMA_CONSISTENT` | | `checkClassVersion` | Determines whether to check the consistency of the class schema. If enabled, Fury checks, writes, and checks consistency using the `classVersionHash`. It will be automatically disabled when `CompatibleMode#COMPATIBLE` is enabled. Disabling is not recommended unless you can ensure the class won't evolve. | `false` | | `checkJdkClassSerializable` | Enables or disables checking of `Serializable` interface for classes under `java.*`. If a class under `java.*` is not `Serializable`, Fury will throw an `UnsupportedOperationException`. | `true` | | `registerGuavaTypes` | Whether to pre-register Guava types such as `RegularImmutableMap`/`RegularImmutableList`. These types are not public API, but seem pretty stable. | `true` | From ff1ab72365fd00bce9ca6b949923281b7332da8b Mon Sep 17 00:00:00 2001 From: orisgarno <42269241+orisgarno@users.noreply.github.com> Date: Mon, 6 Jan 2025 23:20:31 +0700 Subject: [PATCH 06/11] Update java_serialization_guide.md --- docs/guide/java_serialization_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/java_serialization_guide.md b/docs/guide/java_serialization_guide.md index d985e59118..46e9a4021d 100644 --- a/docs/guide/java_serialization_guide.md +++ b/docs/guide/java_serialization_guide.md @@ -512,7 +512,7 @@ serialization error, it may be caused by class inconsistency between serializati If you create fury and set `CompatibleMode` to `org.apache.fury.config.CompatibleMode.COMPATIBLE`, you need to register the class that you want to serialize or deserialize(You don't need to register any class inside that class). -[See example here](https://github.com/apache/fury/blob/main/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java) +[See example here](java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java) In such cases, you can invoke `FuryBuilder#withClassVersionCheck` to create fury to validate it, if deserialization throws `org.apache.fury.exception.ClassNotCompatibleException`, it shows class are inconsistent, and you should create From f5cc36162687d7766d5f3e5bf992b3de87a15a65 Mon Sep 17 00:00:00 2001 From: orisgarno <42269241+orisgarno@users.noreply.github.com> Date: Mon, 6 Jan 2025 23:21:12 +0700 Subject: [PATCH 07/11] Update java_serialization_guide.md --- docs/guide/java_serialization_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/java_serialization_guide.md b/docs/guide/java_serialization_guide.md index 46e9a4021d..26d92ca7f7 100644 --- a/docs/guide/java_serialization_guide.md +++ b/docs/guide/java_serialization_guide.md @@ -512,7 +512,7 @@ serialization error, it may be caused by class inconsistency between serializati If you create fury and set `CompatibleMode` to `org.apache.fury.config.CompatibleMode.COMPATIBLE`, you need to register the class that you want to serialize or deserialize(You don't need to register any class inside that class). -[See example here](java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java) +[See example here](/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java) In such cases, you can invoke `FuryBuilder#withClassVersionCheck` to create fury to validate it, if deserialization throws `org.apache.fury.exception.ClassNotCompatibleException`, it shows class are inconsistent, and you should create From bd0e757fad4f94a16ff8904601287637770b7df9 Mon Sep 17 00:00:00 2001 From: oris garno Date: Tue, 7 Jan 2025 18:13:29 +0700 Subject: [PATCH 08/11] fix: documentation --- docs/guide/java_serialization_guide.md | 10 ++++++---- ...java => DifferentPOJOCompatibleSerializerTest.java} | 2 +- .../type/{DataTypeTest.java => DataTypesTest.java} | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) rename java/fury-core/src/test/java/org/apache/fury/serializer/compatible/{CompatibleSerializerTest.java => DifferentPOJOCompatibleSerializerTest.java} (98%) rename java/fury-format/src/test/java/org/apache/fury/format/type/{DataTypeTest.java => DataTypesTest.java} (98%) diff --git a/docs/guide/java_serialization_guide.md b/docs/guide/java_serialization_guide.md index 26d92ca7f7..eeb3a7cd86 100644 --- a/docs/guide/java_serialization_guide.md +++ b/docs/guide/java_serialization_guide.md @@ -510,10 +510,6 @@ If you create fury without setting `CompatibleMode` to `org.apache.fury.config.C strange serialization error, it may be caused by class inconsistency between serialization peer and deserialization peer. -If you create fury and set `CompatibleMode` to `org.apache.fury.config.CompatibleMode.COMPATIBLE`, you need to register -the class that you want to serialize or deserialize(You don't need to register any class inside that class). -[See example here](/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java) - In such cases, you can invoke `FuryBuilder#withClassVersionCheck` to create fury to validate it, if deserialization throws `org.apache.fury.exception.ClassNotCompatibleException`, it shows class are inconsistent, and you should create fury with @@ -522,6 +518,12 @@ fury with `CompatibleMode.COMPATIBLE` has more performance and space cost, do not set it by default if your classes are always consistent between serialization and deserialization. +### Different POJO deserialization +Fury allows you to serialize one POJO and deserialize it into a different POJO. To achieve this, configure Fury with +`CompatibleMode` set to `org.apache.fury.config.CompatibleMode.COMPATIBLE`. Additionally, you only need to register the +specific classes you want to serialize or deserialize; there's no need to register any nested classes within them. +[See example here](/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/DifferentPOJOCompatibleSerializerTest.java) + ### Use wrong API for deserialization If you serialize an object by invoking `Fury#serialize`, you should invoke `Fury#deserialize` for deserialization diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/DifferentPOJOCompatibleSerializerTest.java similarity index 98% rename from java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java rename to java/fury-core/src/test/java/org/apache/fury/serializer/compatible/DifferentPOJOCompatibleSerializerTest.java index 6f4f1354f5..3a7436d9c2 100644 --- a/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/CompatibleSerializerTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/DifferentPOJOCompatibleSerializerTest.java @@ -32,7 +32,7 @@ * class with different name - scrambled field order to make sure it could handle different field * order - missing or extra field from source class to target class - generic class */ -public class CompatibleSerializerTest extends Assert { +public class DifferentPOJOCompatibleSerializerTest extends Assert { Fury getFury(Class... classes) { Fury instance = diff --git a/java/fury-format/src/test/java/org/apache/fury/format/type/DataTypeTest.java b/java/fury-format/src/test/java/org/apache/fury/format/type/DataTypesTest.java similarity index 98% rename from java/fury-format/src/test/java/org/apache/fury/format/type/DataTypeTest.java rename to java/fury-format/src/test/java/org/apache/fury/format/type/DataTypesTest.java index 866637cb7b..193bcfaae3 100644 --- a/java/fury-format/src/test/java/org/apache/fury/format/type/DataTypeTest.java +++ b/java/fury-format/src/test/java/org/apache/fury/format/type/DataTypesTest.java @@ -27,7 +27,7 @@ import org.apache.fury.test.bean.BeanA; import org.testng.annotations.Test; -public class DataTypeTest { +public class DataTypesTest { @Test public void parseSchema() { From 3e2325f3917dffaf39cdb607aa1d24164befbecc Mon Sep 17 00:00:00 2001 From: oris garno Date: Tue, 7 Jan 2025 18:17:05 +0700 Subject: [PATCH 09/11] fix: documentation --- docs/guide/java_serialization_guide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/guide/java_serialization_guide.md b/docs/guide/java_serialization_guide.md index eeb3a7cd86..588042bc93 100644 --- a/docs/guide/java_serialization_guide.md +++ b/docs/guide/java_serialization_guide.md @@ -519,6 +519,7 @@ fury with consistent between serialization and deserialization. ### Different POJO deserialization + Fury allows you to serialize one POJO and deserialize it into a different POJO. To achieve this, configure Fury with `CompatibleMode` set to `org.apache.fury.config.CompatibleMode.COMPATIBLE`. Additionally, you only need to register the specific classes you want to serialize or deserialize; there's no need to register any nested classes within them. From 1e28057149598fdcefc29e20bc93efe6d98ed35c Mon Sep 17 00:00:00 2001 From: orisgarno <42269241+orisgarno@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:22:43 +0700 Subject: [PATCH 10/11] Update docs/guide/java_serialization_guide.md Co-authored-by: Shawn Yang --- docs/guide/java_serialization_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/java_serialization_guide.md b/docs/guide/java_serialization_guide.md index 588042bc93..c5c8320690 100644 --- a/docs/guide/java_serialization_guide.md +++ b/docs/guide/java_serialization_guide.md @@ -518,7 +518,7 @@ fury with `CompatibleMode.COMPATIBLE` has more performance and space cost, do not set it by default if your classes are always consistent between serialization and deserialization. -### Different POJO deserialization +### Deserialize POJO into another type Fury allows you to serialize one POJO and deserialize it into a different POJO. To achieve this, configure Fury with `CompatibleMode` set to `org.apache.fury.config.CompatibleMode.COMPATIBLE`. Additionally, you only need to register the From 8ed6e84aee0ea87f5ffc09d51f20041103c3ed63 Mon Sep 17 00:00:00 2001 From: orisgarno <42269241+orisgarno@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:22:49 +0700 Subject: [PATCH 11/11] Update docs/guide/java_serialization_guide.md Co-authored-by: Shawn Yang --- docs/guide/java_serialization_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/java_serialization_guide.md b/docs/guide/java_serialization_guide.md index c5c8320690..707643935b 100644 --- a/docs/guide/java_serialization_guide.md +++ b/docs/guide/java_serialization_guide.md @@ -522,7 +522,7 @@ consistent between serialization and deserialization. Fury allows you to serialize one POJO and deserialize it into a different POJO. To achieve this, configure Fury with `CompatibleMode` set to `org.apache.fury.config.CompatibleMode.COMPATIBLE`. Additionally, you only need to register the -specific classes you want to serialize or deserialize; there's no need to register any nested classes within them. +specific classes you want to serialize or deserialize to setup type mapping relationship; there's no need to register any nested classes within them. [See example here](/java/fury-core/src/test/java/org/apache/fury/serializer/compatible/DifferentPOJOCompatibleSerializerTest.java) ### Use wrong API for deserialization