|
39 | 39 | import java.util.LinkedList; |
40 | 40 | import java.util.List; |
41 | 41 | import java.util.Map; |
| 42 | +import java.util.TreeMap; |
| 43 | +import java.util.TreeSet; |
42 | 44 | import java.util.Vector; |
43 | 45 | import java.util.concurrent.ConcurrentHashMap; |
44 | 46 | import lombok.EqualsAndHashCode; |
45 | 47 | import org.apache.fory.Fory; |
46 | 48 | import org.apache.fory.ForyTestBase; |
| 49 | +import org.apache.fory.builder.Generated; |
47 | 50 | import org.apache.fory.config.CompatibleMode; |
48 | 51 | import org.apache.fory.config.Language; |
49 | 52 | import org.apache.fory.memory.MemoryBuffer; |
| 53 | +import org.apache.fory.reflect.ReflectionUtils; |
50 | 54 | import org.apache.fory.util.Preconditions; |
51 | 55 | import org.testng.Assert; |
52 | 56 | import org.testng.annotations.DataProvider; |
@@ -1136,6 +1140,101 @@ public void testNestedObjectSerialization(CompatibleMode compatible) { |
1136 | 1140 | assertEquals(result.nestedList.get(1).nestedValue, "list2"); |
1137 | 1141 | } |
1138 | 1142 |
|
| 1143 | + public static class AsyncTreeSetSubclass extends TreeSet<String> { |
| 1144 | + public AsyncTreeSetSubclass() {} |
| 1145 | + } |
| 1146 | + |
| 1147 | + public static class AsyncTreeMapSubclass extends TreeMap<String, String> { |
| 1148 | + public AsyncTreeMapSubclass() {} |
| 1149 | + } |
| 1150 | + |
| 1151 | + @EqualsAndHashCode |
| 1152 | + public static class AsyncLayerJitContainer implements Serializable { |
| 1153 | + private String name; |
| 1154 | + private AsyncTreeSetSubclass values; |
| 1155 | + private AsyncTreeMapSubclass attributes; |
| 1156 | + |
| 1157 | + public AsyncLayerJitContainer() {} |
| 1158 | + |
| 1159 | + public AsyncLayerJitContainer( |
| 1160 | + String name, AsyncTreeSetSubclass values, AsyncTreeMapSubclass attributes) { |
| 1161 | + this.name = name; |
| 1162 | + this.values = values; |
| 1163 | + this.attributes = attributes; |
| 1164 | + } |
| 1165 | + |
| 1166 | + private void writeObject(ObjectOutputStream s) throws IOException { |
| 1167 | + s.defaultWriteObject(); |
| 1168 | + } |
| 1169 | + |
| 1170 | + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { |
| 1171 | + s.defaultReadObject(); |
| 1172 | + } |
| 1173 | + } |
| 1174 | + |
| 1175 | + @Test(timeOut = 60000) |
| 1176 | + public void testAsyncCompilationNestedTreeCollectionsCompatibleMode() throws InterruptedException { |
| 1177 | + Fory fory = newCompatibleAsyncObjectStreamFory(true); |
| 1178 | + fory.registerSerializer( |
| 1179 | + AsyncLayerJitContainer.class, new ObjectStreamSerializer(fory, AsyncLayerJitContainer.class)); |
| 1180 | + fory.registerSerializer( |
| 1181 | + AsyncTreeSetSubclass.class, new ObjectStreamSerializer(fory, AsyncTreeSetSubclass.class)); |
| 1182 | + fory.registerSerializer( |
| 1183 | + AsyncTreeMapSubclass.class, new ObjectStreamSerializer(fory, AsyncTreeMapSubclass.class)); |
| 1184 | + |
| 1185 | + AsyncTreeSetSubclass values = new AsyncTreeSetSubclass(); |
| 1186 | + values.add("one"); |
| 1187 | + values.add("two"); |
| 1188 | + AsyncTreeMapSubclass attributes = new AsyncTreeMapSubclass(); |
| 1189 | + attributes.put("alpha", "A"); |
| 1190 | + attributes.put("beta", "B"); |
| 1191 | + AsyncLayerJitContainer obj = new AsyncLayerJitContainer("container", values, attributes); |
| 1192 | + |
| 1193 | + serDeCheckSerializer(fory, obj, "ObjectStreamSerializer"); |
| 1194 | + |
| 1195 | + waitForGeneratedLayerSerializer(fory, AsyncLayerJitContainer.class); |
| 1196 | + waitForGeneratedLayerSerializer(fory, AsyncTreeSetSubclass.class); |
| 1197 | + waitForGeneratedLayerSerializer(fory, AsyncTreeMapSubclass.class); |
| 1198 | + |
| 1199 | + serDeCheckSerializer(fory, obj, "ObjectStreamSerializer"); |
| 1200 | + } |
| 1201 | + |
| 1202 | + @Test(timeOut = 60000) |
| 1203 | + public void testAsyncCompilationTreeSetSubclassObjectStreamSerializer() throws InterruptedException { |
| 1204 | + Fory fory = newCompatibleAsyncObjectStreamFory(true); |
| 1205 | + fory.registerSerializer( |
| 1206 | + AsyncTreeSetSubclass.class, new ObjectStreamSerializer(fory, AsyncTreeSetSubclass.class)); |
| 1207 | + |
| 1208 | + AsyncTreeSetSubclass values = new AsyncTreeSetSubclass(); |
| 1209 | + values.add("one"); |
| 1210 | + values.add("two"); |
| 1211 | + |
| 1212 | + serDeCheckSerializer(fory, values, "ObjectStreamSerializer"); |
| 1213 | + waitForGeneratedLayerSerializer(fory, AsyncTreeSetSubclass.class); |
| 1214 | + serDeCheckSerializer(fory, values, "ObjectStreamSerializer"); |
| 1215 | + } |
| 1216 | + |
| 1217 | + @Test |
| 1218 | + public void testTreeCollectionsStillWorkWithoutAsyncCompilation() { |
| 1219 | + Fory fory = newCompatibleAsyncObjectStreamFory(false); |
| 1220 | + fory.registerSerializer( |
| 1221 | + AsyncLayerJitContainer.class, new ObjectStreamSerializer(fory, AsyncLayerJitContainer.class)); |
| 1222 | + fory.registerSerializer( |
| 1223 | + AsyncTreeSetSubclass.class, new ObjectStreamSerializer(fory, AsyncTreeSetSubclass.class)); |
| 1224 | + fory.registerSerializer( |
| 1225 | + AsyncTreeMapSubclass.class, new ObjectStreamSerializer(fory, AsyncTreeMapSubclass.class)); |
| 1226 | + |
| 1227 | + AsyncTreeSetSubclass values = new AsyncTreeSetSubclass(); |
| 1228 | + values.add("one"); |
| 1229 | + values.add("two"); |
| 1230 | + AsyncTreeMapSubclass attributes = new AsyncTreeMapSubclass(); |
| 1231 | + attributes.put("alpha", "A"); |
| 1232 | + attributes.put("beta", "B"); |
| 1233 | + |
| 1234 | + serDeCheckSerializer( |
| 1235 | + fory, new AsyncLayerJitContainer("container", values, attributes), "ObjectStreamSerializer"); |
| 1236 | + } |
| 1237 | + |
1139 | 1238 | // ==================== Circular Reference in Custom Serialization ==================== |
1140 | 1239 |
|
1141 | 1240 | /** Class with potential circular reference. */ |
@@ -1279,4 +1378,44 @@ public void testAllPrimitiveTypes(CompatibleMode compatible) { |
1279 | 1378 | assertEquals(result.charVal, 'A'); |
1280 | 1379 | assertEquals(result.boolVal, true); |
1281 | 1380 | } |
| 1381 | + |
| 1382 | + private Fory newCompatibleAsyncObjectStreamFory(boolean asyncCompilation) { |
| 1383 | + return Fory.builder() |
| 1384 | + .withLanguage(Language.JAVA) |
| 1385 | + .requireClassRegistration(false) |
| 1386 | + .withRefTracking(true) |
| 1387 | + .withCodegen(true) |
| 1388 | + .withCompatibleMode(CompatibleMode.COMPATIBLE) |
| 1389 | + .withAsyncCompilation(asyncCompilation) |
| 1390 | + .build(); |
| 1391 | + } |
| 1392 | + |
| 1393 | + private void waitForGeneratedLayerSerializer(Fory fory, Class<?> type) throws InterruptedException { |
| 1394 | + long deadline = System.currentTimeMillis() + 30_000; |
| 1395 | + while (System.currentTimeMillis() < deadline) { |
| 1396 | + if (hasGeneratedLayerSerializer(fory, type)) { |
| 1397 | + return; |
| 1398 | + } |
| 1399 | + Thread.sleep(10); |
| 1400 | + } |
| 1401 | + Assert.fail("Timed out waiting for generated layer serializer for " + type.getName()); |
| 1402 | + } |
| 1403 | + |
| 1404 | + private boolean hasGeneratedLayerSerializer(Fory fory, Class<?> type) { |
| 1405 | + Serializer<?> serializer = fory.getTypeResolver().getSerializer(type); |
| 1406 | + if (!(serializer instanceof ObjectStreamSerializer)) { |
| 1407 | + return false; |
| 1408 | + } |
| 1409 | + Object[] slotsInfos = (Object[]) ReflectionUtils.getObjectFieldValue(serializer, "slotsInfos"); |
| 1410 | + if (slotsInfos.length == 0) { |
| 1411 | + return false; |
| 1412 | + } |
| 1413 | + for (Object slotsInfo : slotsInfos) { |
| 1414 | + Object slotsSerializer = ReflectionUtils.getObjectFieldValue(slotsInfo, "slotsSerializer"); |
| 1415 | + if (!(slotsSerializer instanceof Generated)) { |
| 1416 | + return false; |
| 1417 | + } |
| 1418 | + } |
| 1419 | + return true; |
| 1420 | + } |
1282 | 1421 | } |
0 commit comments