Some improvements around EventuallyConsistentMapBuilder serializer handling.

- Allow directly passing KryoNamespace
- Add some registration id gap before ECMap's internal registration
- Some improvements for ease of registration issue investigation
-- Add friendly name to ECMap's internal KryoNamespace
-- Add backtrace information

Change-Id: I7c87b3aefbaea4b2ed12b38c3e0813e9d195c7a9
diff --git a/apps/vtn/vtnrsc/src/test/java/org/onosproject/vtnrsc/util/VtnEventuallyConsistentMapTest.java b/apps/vtn/vtnrsc/src/test/java/org/onosproject/vtnrsc/util/VtnEventuallyConsistentMapTest.java
index af6b0a7..0c52017 100644
--- a/apps/vtn/vtnrsc/src/test/java/org/onosproject/vtnrsc/util/VtnEventuallyConsistentMapTest.java
+++ b/apps/vtn/vtnrsc/src/test/java/org/onosproject/vtnrsc/util/VtnEventuallyConsistentMapTest.java
@@ -182,6 +182,11 @@
         }
 
         @Override
+        public EventuallyConsistentMapBuilder<K, V> withSerializer(KryoNamespace serializer) {
+            return this;
+        }
+
+        @Override
         public EventuallyConsistentMapBuilder<K, V>
         withTimestampProvider(BiFunction<K, V, Timestamp> timestampProvider) {
             return this;
diff --git a/core/api/src/main/java/org/onosproject/store/service/EventuallyConsistentMapBuilder.java b/core/api/src/main/java/org/onosproject/store/service/EventuallyConsistentMapBuilder.java
index cf2c029..ff9aff5 100644
--- a/core/api/src/main/java/org/onosproject/store/service/EventuallyConsistentMapBuilder.java
+++ b/core/api/src/main/java/org/onosproject/store/service/EventuallyConsistentMapBuilder.java
@@ -67,6 +67,20 @@
             KryoNamespace.Builder serializerBuilder);
 
     /**
+     * Sets a serializer that can be used to create a serializer that
+     * can serialize both the keys and values put into the map. The serializer
+     * builder should be pre-populated with any classes that will be put into
+     * the map.
+     * <p>
+     * Note: This is a mandatory parameter.
+     * </p>
+     *
+     * @param serializer serializer
+     * @return this EventuallyConsistentMapBuilder
+     */
+    EventuallyConsistentMapBuilder<K, V> withSerializer(KryoNamespace serializer);
+
+    /**
      * Sets the function to use for generating timestamps for map updates.
      * <p>
      * The client must provide an {@code BiFunction<K, V, Timestamp>}
diff --git a/core/api/src/test/java/org/onosproject/store/service/TestEventuallyConsistentMap.java b/core/api/src/test/java/org/onosproject/store/service/TestEventuallyConsistentMap.java
index b87517a..008ae69 100644
--- a/core/api/src/test/java/org/onosproject/store/service/TestEventuallyConsistentMap.java
+++ b/core/api/src/test/java/org/onosproject/store/service/TestEventuallyConsistentMap.java
@@ -179,6 +179,11 @@
         }
 
         @Override
+        public EventuallyConsistentMapBuilder<K, V> withSerializer(KryoNamespace serializer) {
+            return this;
+        }
+
+        @Override
         public EventuallyConsistentMapBuilder<K, V>
         withTimestampProvider(BiFunction<K, V, Timestamp> timestampProvider) {
             return this;
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/EventuallyConsistentMapBuilderImpl.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/EventuallyConsistentMapBuilderImpl.java
index 51b8f6d..ea7b5d1 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/EventuallyConsistentMapBuilderImpl.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/EventuallyConsistentMapBuilderImpl.java
@@ -42,6 +42,7 @@
     private final ClusterCommunicationService clusterCommunicator;
 
     private String name;
+    private KryoNamespace serializer;
     private KryoNamespace.Builder serializerBuilder;
     private ExecutorService eventExecutor;
     private ExecutorService communicationExecutor;
@@ -85,6 +86,12 @@
     }
 
     @Override
+    public EventuallyConsistentMapBuilder<K, V> withSerializer(KryoNamespace serializer) {
+        this.serializer = checkNotNull(serializer);
+        return this;
+    }
+
+    @Override
     public EventuallyConsistentMapBuilder<K, V> withTimestampProvider(
             BiFunction<K, V, Timestamp> timestampProvider) {
         this.timestampProvider = checkNotNull(timestampProvider);
@@ -147,13 +154,16 @@
     @Override
     public EventuallyConsistentMap<K, V> build() {
         checkNotNull(name, "name is a mandatory parameter");
-        checkNotNull(serializerBuilder, "serializerBuilder is a mandatory parameter");
         checkNotNull(timestampProvider, "timestampProvider is a mandatory parameter");
+        if (serializer == null && serializerBuilder != null) {
+            serializer = serializerBuilder.build(name);
+        }
+        checkNotNull(serializer, "serializer is a mandatory parameter");
 
         return new EventuallyConsistentMapImpl<>(name,
                                                  clusterService,
                                                  clusterCommunicator,
-                                                 serializerBuilder,
+                                                 serializer,
                                                  timestampProvider,
                                                  peerUpdateFunction,
                                                  eventExecutor,
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/EventuallyConsistentMapImpl.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/EventuallyConsistentMapImpl.java
index 2374691..7a9e655 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/EventuallyConsistentMapImpl.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/EventuallyConsistentMapImpl.java
@@ -58,7 +58,6 @@
 import org.onosproject.store.service.EventuallyConsistentMap;
 import org.onosproject.store.service.EventuallyConsistentMapEvent;
 import org.onosproject.store.service.EventuallyConsistentMapListener;
-import org.onosproject.store.service.Serializer;
 import org.onosproject.store.service.WallClockTimestamp;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -138,7 +137,7 @@
      * @param mapName               a String identifier for the map.
      * @param clusterService        the cluster service
      * @param clusterCommunicator   the cluster communications service
-     * @param serializerBuilder     a Kryo namespace builder that can serialize
+     * @param serializer            a Kryo namespace that can serialize
      *                              both K and V
      * @param timestampProvider     provider of timestamps for K and V
      * @param peerUpdateFunction    function that provides a set of nodes to immediately
@@ -159,7 +158,7 @@
     EventuallyConsistentMapImpl(String mapName,
                                 ClusterService clusterService,
                                 ClusterCommunicationService clusterCommunicator,
-                                KryoNamespace.Builder serializerBuilder,
+                                KryoNamespace ns,
                                 BiFunction<K, V, Timestamp> timestampProvider,
                                 BiFunction<K, V, Collection<NodeId>> peerUpdateFunction,
                                 ExecutorService eventExecutor,
@@ -172,25 +171,14 @@
                                 boolean persistent,
                                 PersistenceService persistenceService) {
         this.mapName = mapName;
-        this.serializer = createSerializer(serializerBuilder);
+        this.serializer = createSerializer(ns);
         this.persistenceService = persistenceService;
         this.persistent =
                 persistent;
         if (persistent) {
             items = this.persistenceService.<K, MapValue<V>>persistentMapBuilder()
                     .withName(PERSISTENT_LOCAL_MAP_NAME)
-                    .withSerializer(new Serializer() {
-
-                        @Override
-                        public <T> byte[] encode(T object) {
-                            return EventuallyConsistentMapImpl.this.serializer.encode(object);
-                        }
-
-                        @Override
-                        public <T> T decode(byte[] bytes) {
-                            return EventuallyConsistentMapImpl.this.serializer.decode(bytes);
-                        }
-                    })
+                    .withSerializer(this.serializer)
                     .build();
         } else {
             items = Maps.newConcurrentMap();
@@ -268,18 +256,21 @@
         this.lightweightAntiEntropy = !convergeFaster;
     }
 
-    private StoreSerializer createSerializer(KryoNamespace.Builder builder) {
-        return StoreSerializer.using(builder
-                        .register(KryoNamespaces.BASIC)
-                        .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
-                        .register(LogicalTimestamp.class)
-                        .register(WallClockTimestamp.class)
-                        .register(AntiEntropyAdvertisement.class)
-                        .register(AntiEntropyResponse.class)
-                        .register(UpdateEntry.class)
-                        .register(MapValue.class)
-                        .register(MapValue.Digest.class)
-                        .build(name()));
+    private StoreSerializer createSerializer(KryoNamespace ns) {
+        return StoreSerializer.using(KryoNamespace.newBuilder()
+                         .register(ns)
+                         // not so robust way to avoid collision with other
+                         // user supplied registrations
+                         .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 100)
+                         .register(KryoNamespaces.BASIC)
+                         .register(LogicalTimestamp.class)
+                         .register(WallClockTimestamp.class)
+                         .register(AntiEntropyAdvertisement.class)
+                         .register(AntiEntropyResponse.class)
+                         .register(UpdateEntry.class)
+                         .register(MapValue.class)
+                         .register(MapValue.Digest.class)
+                         .build(name() + "-ecmap"));
     }
 
     @Override
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/StoreSerializer.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/StoreSerializer.java
index af09b31..761e4bc 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/StoreSerializer.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/StoreSerializer.java
@@ -20,11 +20,12 @@
 import java.nio.ByteBuffer;
 
 import org.onlab.util.KryoNamespace;
+import org.onosproject.store.service.Serializer;
 
 /**
  * Service to serialize Objects into byte array.
  */
-public interface StoreSerializer {
+public interface StoreSerializer extends Serializer {
 
     /**
      * Serializes the specified object into bytes.
@@ -32,6 +33,7 @@
      * @param obj object to be serialized
      * @return serialized bytes
      */
+    @Override
     byte[] encode(final Object obj);
 
     /**
@@ -57,6 +59,7 @@
      * @return deserialized object
      * @param <T> decoded type
      */
+    @Override
     <T> T decode(final byte[] bytes);
 
     /**
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 70fdfc8..d1f95c0 100644
--- a/utils/misc/src/main/java/org/onlab/util/KryoNamespace.java
+++ b/utils/misc/src/main/java/org/onlab/util/KryoNamespace.java
@@ -125,9 +125,11 @@
             if (!types.isEmpty()) {
                 if (id != FLOATING_ID && id < blockHeadId + types.size()) {
 
-                    log.warn("requested nextId {} could potentially overlap " +
-                             "with existing registrations {}+{} ",
-                             id, blockHeadId, types.size());
+                    if (log.isWarnEnabled()) {
+                        log.warn("requested nextId {} could potentially overlap " +
+                                 "with existing registrations {}+{} ",
+                                 id, blockHeadId, types.size(), new RuntimeException());
+                    }
                 }
                 blocks.add(new RegistrationBlock(this.blockHeadId, types));
                 types = new ArrayList<>();