[ONOS-6636] Implement backwards/forwards compatible versioned Kryo serialization
Change-Id: If859ea61473401abd8b8f606aec0f1586a042a2a
diff --git a/core/store/serializers/src/test/java/org/onosproject/store/serializers/KryoSerializerTest.java b/core/store/serializers/src/test/java/org/onosproject/store/serializers/KryoSerializerTest.java
index ed1a473..8217b28 100644
--- a/core/store/serializers/src/test/java/org/onosproject/store/serializers/KryoSerializerTest.java
+++ b/core/store/serializers/src/test/java/org/onosproject/store/serializers/KryoSerializerTest.java
@@ -27,6 +27,7 @@
import org.onlab.packet.VlanId;
import org.onlab.util.Bandwidth;
import org.onlab.util.Frequency;
+import org.onlab.util.KryoNamespace;
import org.onosproject.cluster.NodeId;
import org.onosproject.cluster.RoleInfo;
import org.onosproject.core.DefaultApplicationId;
@@ -128,6 +129,10 @@
}
private byte[] serialize(Object object) {
+ return serialize(object, serializer);
+ }
+
+ private byte[] serialize(Object object, StoreSerializer serializer) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
serializer.encode(object, buffer);
buffer.flip();
@@ -136,6 +141,10 @@
return bytes;
}
+ private <T> T deserialize(byte[] bytes, StoreSerializer serializer) {
+ return serializer.decode(bytes);
+ }
+
private <T> void testBytesEqual(T expected, T actual) {
byte[] expectedBytes = serialize(expected);
byte[] actualBytes = serialize(actual);
@@ -161,6 +170,63 @@
assertNotNull(copy);
}
+ public static class Versioned1 {
+ private int value1;
+ }
+
+ public static class Versioned2 {
+ private int value1;
+ private int value2;
+ private int value3;
+ }
+
+ public static class Versioned3 {
+ private int value1;
+ private int value3;
+ }
+
+ @Test
+ public void testVersioned() {
+ StoreSerializer serializer1 = StoreSerializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(Versioned1.class)
+ .build());
+
+ StoreSerializer serializer2 = StoreSerializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(Versioned2.class)
+ .build());
+
+ StoreSerializer serializer3 = StoreSerializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(Versioned3.class)
+ .build());
+
+ Versioned1 versioned1 = new Versioned1();
+ versioned1.value1 = 1;
+
+ Versioned2 versioned2 = new Versioned2();
+ versioned2.value1 = 1;
+ versioned2.value2 = 2;
+ versioned2.value3 = 3;
+
+ Versioned3 versioned3 = new Versioned3();
+ versioned3.value1 = 1;
+ versioned3.value3 = 3;
+
+ Versioned2 versioned1Upgrade = deserialize(serialize(versioned1, serializer1), serializer2);
+ assertEquals(versioned1.value1, versioned1Upgrade.value1);
+
+ Versioned1 versioned2Downgrade = deserialize(serialize(versioned2, serializer2), serializer1);
+ assertEquals(versioned2.value1, versioned2Downgrade.value1);
+
+ Versioned3 versioned2Upgrade = deserialize(serialize(versioned2, serializer2), serializer3);
+ assertEquals(versioned2.value1, versioned2Upgrade.value1);
+ assertEquals(versioned2.value3, versioned2Upgrade.value3);
+
+ Versioned2 versioned3Downgrade = deserialize(serialize(versioned3, serializer3), serializer2);
+ assertEquals(versioned3.value1, versioned3Downgrade.value1);
+ }
@Test
public void testConnectPoint() {
diff --git a/utils/misc/src/main/java/org/onlab/util/KryoNamespace.java b/utils/misc/src/main/java/org/onlab/util/KryoNamespace.java
index 1eb1569..a38d29c 100644
--- a/utils/misc/src/main/java/org/onlab/util/KryoNamespace.java
+++ b/utils/misc/src/main/java/org/onlab/util/KryoNamespace.java
@@ -25,6 +25,7 @@
import com.esotericsoftware.kryo.pool.KryoCallback;
import com.esotericsoftware.kryo.pool.KryoFactory;
import com.esotericsoftware.kryo.pool.KryoPool;
+import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import org.apache.commons.lang3.tuple.Pair;
@@ -421,6 +422,7 @@
log.trace("Creating Kryo instance for {}", this);
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(registrationRequired);
+ kryo.setDefaultSerializer(CompatibleFieldSerializer::new);
// TODO rethink whether we want to use StdInstantiatorStrategy
kryo.setInstantiatorStrategy(