[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(