Creating new creators for multimap primitive and a name based getter for treemap primitive.

Change-Id: I981b3f1f8ee01fbdd0677c3eedc3d5024b8bcf1e
diff --git a/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveCreator.java b/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveCreator.java
index b0a25f2..2e986c4 100644
--- a/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveCreator.java
+++ b/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveCreator.java
@@ -20,6 +20,7 @@
 import org.onosproject.store.service.AsyncAtomicCounter;
 import org.onosproject.store.service.AsyncAtomicValue;
 import org.onosproject.store.service.AsyncConsistentMap;
+import org.onosproject.store.service.AsyncConsistentMultimap;
 import org.onosproject.store.service.AsyncConsistentTreeMap;
 import org.onosproject.store.service.AsyncDistributedSet;
 import org.onosproject.store.service.AsyncDocumentTree;
@@ -51,7 +52,20 @@
      * @param <V> value type
      * @return distributedTreeMap
      */
-    <V> AsyncConsistentTreeMap<V> newAsyncConsistentTreeMap(String name, Serializer serializer);
+    <V> AsyncConsistentTreeMap<V> newAsyncConsistentTreeMap(
+            String name, Serializer serializer);
+
+    /**
+     * Creates a new set backed {@code AsyncConsistentMultimap}.
+     *
+     * @param name multimap name
+     * @param serializer serializer to use for serializing/deserializing
+     * @param <K> key type
+     * @param <V> value type
+     * @return set backed distributedMultimap
+     */
+    <K, V> AsyncConsistentMultimap<K, V> newAsyncConsistentSetMultimap(
+            String name, Serializer serializer);
 
     /**
      * Creates a new {@code AsyncAtomicCounter}.
diff --git a/core/api/src/main/java/org/onosproject/store/service/AsyncConsistentMultimap.java b/core/api/src/main/java/org/onosproject/store/service/AsyncConsistentMultimap.java
index a0f3b84..da121c9 100644
--- a/core/api/src/main/java/org/onosproject/store/service/AsyncConsistentMultimap.java
+++ b/core/api/src/main/java/org/onosproject/store/service/AsyncConsistentMultimap.java
@@ -17,6 +17,7 @@
 package org.onosproject.store.service;
 
 import com.google.common.collect.Multiset;
+import org.onosproject.store.primitives.DefaultConsistentMultimap;
 
 import java.util.Collection;
 import java.util.Map;
@@ -229,4 +230,32 @@
      * values, the returned map may be empty.
      */
     CompletableFuture<Map<K, Collection<V>>> asMap();
+
+    /**
+     * Returns a {@code ConsistentMultimap} instance that wraps this map. All
+     * calls will have the same behavior as this map but will be blocking
+     * instead of asynchronous. If a call does not complete within the
+     * default timeout an exception will be produced.
+     *
+     * @return a {@code ConsistentMultimap} which wraps this map, providing
+     * synchronous access to this map
+     */
+    default ConsistentMultimap<K, V> asMultimap() {
+        return asMultimap(DEFAULT_OPERTATION_TIMEOUT_MILLIS);
+    }
+
+    /**
+     * Returns a {@code ConsistentMultimap} instance that wraps this map. All
+     * calls will have the same behavior as this map but will be blocking
+     * instead of asynchronous. If a call does not complete within the
+     * specified timeout an exception will be produced.
+     *
+     * @param timeoutMillis the number of millis to block while waiting for a
+     *                      call to return
+     * @return a {@code ConsistentMultimap} which wraps this map, providing
+     * synchronous access to this map
+     */
+    default ConsistentMultimap<K, V> asMultimap(long timeoutMillis) {
+        return new DefaultConsistentMultimap(this, timeoutMillis);
+    }
 }
diff --git a/core/api/src/main/java/org/onosproject/store/service/ConsistentMultimapBuilder.java b/core/api/src/main/java/org/onosproject/store/service/ConsistentMultimapBuilder.java
new file mode 100644
index 0000000..1881d795
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/store/service/ConsistentMultimapBuilder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.store.service;
+
+import org.onosproject.store.primitives.DistributedPrimitiveBuilder;
+
+/**
+ * A builder class for {@code AsyncConsistentMultimap}.
+ */
+public abstract class ConsistentMultimapBuilder<K, V>
+        extends DistributedPrimitiveBuilder<ConsistentMultimapBuilder<K, V>,
+        ConsistentMultimap<K, V>> {
+
+    private boolean purgeOnUninstall = false;
+
+    public ConsistentMultimapBuilder() {
+        super(DistributedPrimitive.Type.CONSISTENT_MULTIMAP);
+    }
+
+    /**
+     * Clears multimap contents when the owning application is uninstalled.
+     *
+     * @return this builder
+     */
+    public ConsistentMultimapBuilder<K, V> withPurgeOnUninstall() {
+        purgeOnUninstall = true;
+        return this;
+    }
+
+    /**
+     * Returns if multimap entries need to be cleared when owning application
+     * is uninstalled.
+     *
+     * @return true if items are to be cleared on uninstall
+     */
+    public boolean purgeOnUninstall() {
+        return purgeOnUninstall;
+    }
+
+    /**
+     * Builds the distributed multimap based on the configuration options
+     * supplied to this builder.
+     *
+     * @return new distributed multimap
+     * @throws java.lang.RuntimeException if a mandatory parameter is missing
+     */
+    public abstract AsyncConsistentMultimap<K, V> buildMultimap();
+}
diff --git a/core/api/src/main/java/org/onosproject/store/service/StorageService.java b/core/api/src/main/java/org/onosproject/store/service/StorageService.java
index 21fdfda..d532be2 100644
--- a/core/api/src/main/java/org/onosproject/store/service/StorageService.java
+++ b/core/api/src/main/java/org/onosproject/store/service/StorageService.java
@@ -60,6 +60,15 @@
     <V> ConsistentTreeMapBuilder<V> consistentTreeMapBuilder();
 
     /**
+     * Creates a new {@code AsyncConsistentSetMultimapBuilder}.
+     *
+     * @param <K> key type
+     * @param <V> value type
+     * @return builder for a set based async consistent multimap
+     */
+    <K, V> ConsistentMultimapBuilder<K, V> consistentMultimapBuilder();
+
+    /**
      * Creates a new DistributedSetBuilder.
      *
      * @param <E> set element type
@@ -136,6 +145,30 @@
      */
     <V> AsyncDocumentTree<V> getDocumentTree(String name, Serializer serializer);
 
+     /** Returns a set backed instance of {@code AsyncConsistentMultimap} with
+     * the specified name.
+     *
+     * @param name the multimap name
+     * @param serializer serializer
+     * @param <K> key type
+     * @param <V> value type
+     * @return set backed {@code AsyncConsistentMultimap} instance
+     */
+    <K, V> AsyncConsistentMultimap<K, V> getAsyncSetMultimap(String name,
+                                                             Serializer serializer);
+
+    /**
+     * Returns an instance of {@code AsyncConsistentTreeMap} with the specified
+     * name.
+     *
+     * @param name the treemap name
+     * @param serializer serializer
+     * @param <V> value type
+     * @return set backed {@code AsyncConsistentTreeMap} instance
+     */
+    <V> AsyncConsistentTreeMap<V> getAsyncTreeMap(String name,
+                                                  Serializer serializer);
+
     /**
      * Returns an instance of {@code Topic} with specified name.
      *
diff --git a/core/api/src/test/java/org/onosproject/store/service/StorageServiceAdapter.java b/core/api/src/test/java/org/onosproject/store/service/StorageServiceAdapter.java
index 29dd72d..c44d3c7 100644
--- a/core/api/src/test/java/org/onosproject/store/service/StorageServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/store/service/StorageServiceAdapter.java
@@ -65,11 +65,23 @@
     }
 
     @Override
+    public <K, V> AsyncConsistentMultimap<K, V> getAsyncSetMultimap(
+            String name, Serializer serializer) {
+        return null;
+    }
+
+    @Override
     public <T> Topic<T> getTopic(String name, Serializer serializer) {
         return null;
     }
 
     @Override
+    public <V> AsyncConsistentTreeMap<V> getAsyncTreeMap(
+            String name, Serializer serializer) {
+        return null;
+    }
+
+    @Override
     public <V> ConsistentTreeMapBuilder<V> consistentTreeMapBuilder() {
         return null;
     }
@@ -78,4 +90,8 @@
     public <V> AsyncDocumentTree<V> getDocumentTree(String name, Serializer serializer) {
         return null;
     }
+    @Override
+    public <K, V> ConsistentMultimapBuilder<K, V> consistentMultimapBuilder() {
+        return null;
+    }
 }
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java
new file mode 100644
index 0000000..976862b
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.store.primitives.impl;
+
+import org.onosproject.store.primitives.DistributedPrimitiveCreator;
+import org.onosproject.store.service.AsyncConsistentMultimap;
+import org.onosproject.store.service.ConsistentMultimap;
+import org.onosproject.store.service.ConsistentMultimapBuilder;
+
+/**
+ * Default {@link AsyncConsistentMultimap} builder.
+ */
+public class DefaultConsistentMultimapBuilder<K, V>
+        extends ConsistentMultimapBuilder<K, V> {
+
+    private final DistributedPrimitiveCreator primitiveCreator;
+
+    public DefaultConsistentMultimapBuilder(
+            DistributedPrimitiveCreator primitiveCreator) {
+        this.primitiveCreator = primitiveCreator;
+    }
+
+    @Override
+    public AsyncConsistentMultimap<K, V> buildMultimap() {
+        return primitiveCreator.newAsyncConsistentSetMultimap(
+                name(), serializer());
+    }
+
+    @Override
+    public ConsistentMultimap<K, V> build() {
+        return buildMultimap().asMultimap();
+    }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java
index 2aa906a..65a364e 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java
@@ -35,7 +35,8 @@
 
     @Override
     public AsyncConsistentTreeMap<V> buildTreeMap() {
-        return primitiveCreator.newAsyncConsistentTreeMap(name(), serializer());
+        return primitiveCreator.newAsyncConsistentTreeMap(name(),
+                                                          serializer());
     }
 
     @Override
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DelegatingAsyncConsistentMultimap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DelegatingAsyncConsistentMultimap.java
new file mode 100644
index 0000000..a425c32
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DelegatingAsyncConsistentMultimap.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.store.primitives.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Multiset;
+import org.onosproject.store.service.AsyncConsistentMultimap;
+import org.onosproject.store.service.Versioned;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+
+/**
+ * {@code AsyncConsistentMultimap} that merely delegates control to
+ * another AsyncConsistentMultimap.
+ *
+ * @param <K> key type
+ * @param <V> value type
+ */
+public class DelegatingAsyncConsistentMultimap<K, V>
+        implements AsyncConsistentMultimap<K, V> {
+
+    private final AsyncConsistentMultimap<K, V> delegateMap;
+
+    public DelegatingAsyncConsistentMultimap(
+            AsyncConsistentMultimap<K, V> delegateMap) {
+        this.delegateMap = Preconditions.checkNotNull(delegateMap);
+    }
+
+    @Override
+    public String name() {
+        return delegateMap.name();
+    }
+
+    @Override
+    public CompletableFuture<Integer> size() {
+        return delegateMap.size();
+    }
+
+    @Override
+    public CompletableFuture<Boolean> isEmpty() {
+        return delegateMap.isEmpty();
+    }
+
+    @Override
+    public CompletableFuture<Boolean> containsKey(K key) {
+        return delegateMap.containsKey(key);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> containsValue(V value) {
+        return delegateMap.containsValue(value);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> containsEntry(K key, V value) {
+        return delegateMap.containsEntry(key, value);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> put(K key, V value) {
+        return delegateMap.put(key, value);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> remove(K key, V value) {
+        return delegateMap.remove(key, value);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> removeAll(
+            K key, Collection<? extends V> values) {
+        return delegateMap.removeAll(key, values);
+    }
+
+    @Override
+    public CompletableFuture<Versioned<Collection<? extends V>>>
+        removeAll(K key) {
+        return delegateMap.removeAll(key);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> putAll(
+            K key, Collection<? extends V> values) {
+        return delegateMap.putAll(key, values);
+    }
+
+    @Override
+    public CompletableFuture<Versioned<Collection<? extends V>>>
+        replaceValues(K key, Collection<V> values) {
+        return delegateMap.replaceValues(key, values);
+    }
+
+    @Override
+    public CompletableFuture<Void> clear() {
+        return delegateMap.clear();
+    }
+
+    @Override
+    public CompletableFuture<Versioned<Collection<? extends V>>> get(K key) {
+        return delegateMap.get(key);
+    }
+
+    @Override
+    public CompletableFuture<Set<K>> keySet() {
+        return delegateMap.keySet();
+    }
+
+    @Override
+    public CompletableFuture<Multiset<K>> keys() {
+        return delegateMap.keys();
+    }
+
+    @Override
+    public CompletableFuture<Multiset<V>> values() {
+        return delegateMap.values();
+    }
+
+    @Override
+    public CompletableFuture<Collection<Map.Entry<K, V>>> entries() {
+        return delegateMap.entries();
+    }
+
+    @Override
+    public CompletableFuture<Map<K, Collection<V>>> asMap() {
+        return delegateMap.asMap();
+    }
+
+    @Override
+    public void addStatusChangeListener(Consumer<Status> listener) {
+        delegateMap.addStatusChangeListener(listener);
+    }
+
+    @Override
+    public void removeStatusChangeListener(Consumer<Status> listener) {
+        delegateMap.removeStatusChangeListener(listener);
+    }
+
+    @Override
+    public Collection<Consumer<Status>> statusChangeListeners() {
+        return delegateMap.statusChangeListeners();
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("delegateMap", delegateMap)
+                .toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(delegateMap);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof DelegatingAsyncConsistentMultimap) {
+            DelegatingAsyncConsistentMultimap<K, V> that =
+                    (DelegatingAsyncConsistentMultimap) other;
+            return this.delegateMap.equals(that.delegateMap);
+        }
+        return false;
+    }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DistributedPrimitives.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DistributedPrimitives.java
index 83af62d..520b9a6 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DistributedPrimitives.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DistributedPrimitives.java
@@ -18,6 +18,7 @@
 import java.util.function.Function;
 
 import org.onosproject.store.service.AsyncConsistentMap;
+import org.onosproject.store.service.AsyncConsistentMultimap;
 import org.onosproject.store.service.AsyncConsistentTreeMap;
 import org.onosproject.store.service.AsyncDistributedSet;
 
@@ -121,4 +122,39 @@
                                                        valueEncoder,
                                                        valueDecoder);
     }
+
+    /**
+     * Creates an instance of {@code AsyncConsistentMultimap} that transforms
+     * operations inputs and applies them to corresponding operation in a
+     * differently typed map and returns the output after reverse transforming
+     * it.
+     *
+     * @param multimap backing multimap
+     * @param keyEncoder transformer for key type of returned map to key type
+     *                   of input map
+     * @param keyDecoder transformer for key type of input map to key type of
+     *                   returned map
+     * @param valueEncoder transformer for value type of returned map to value
+     *                     type of input map
+     * @param valueDecoder transformer for value type of input map to value
+     *                     type of returned map
+     * @param <K1> returned map key type
+     * @param <K2> input map key type
+     * @param <V1> returned map value type
+     * @param <V2> input map key type
+     * @return new map
+     */
+    public static <K1, V1, K2, V2> AsyncConsistentMultimap<K1, V1>
+    newTranscodingMultimap(AsyncConsistentMultimap<K2, V2> multimap,
+                           Function<K1, K2> keyEncoder,
+                           Function<K2, K1> keyDecoder,
+                           Function<V1, V2> valueEncoder,
+                           Function<V2, V1> valueDecoder) {
+        return new TranscodingAsyncConsistentMultimap<>(multimap,
+                                                        keyEncoder,
+                                                        keyDecoder,
+                                                        valueDecoder,
+                                                        valueEncoder);
+    }
+
 }
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java
index b36906c..78447a8 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java
@@ -27,6 +27,7 @@
 import org.onosproject.store.service.AsyncAtomicCounter;
 import org.onosproject.store.service.AsyncAtomicValue;
 import org.onosproject.store.service.AsyncConsistentMap;
+import org.onosproject.store.service.AsyncConsistentMultimap;
 import org.onosproject.store.service.AsyncConsistentTreeMap;
 import org.onosproject.store.service.AsyncDistributedSet;
 import org.onosproject.store.service.AsyncDocumentTree;
@@ -76,6 +77,13 @@
     }
 
     @Override
+    public <K, V> AsyncConsistentMultimap<K, V> newAsyncConsistentSetMultimap(
+            String name, Serializer serializer) {
+        return getCreator(name).newAsyncConsistentSetMultimap(name,
+                                                              serializer);
+    }
+
+    @Override
     public <E> AsyncDistributedSet<E> newAsyncDistributedSet(String name, Serializer serializer) {
         return DistributedPrimitives.newSetFromMap(newAsyncConsistentMap(name, serializer));
     }
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageManager.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageManager.java
index 4d9c1a6..e2820a2 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageManager.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageManager.java
@@ -44,10 +44,13 @@
 import org.onosproject.store.service.AsyncAtomicValue;
 import org.onosproject.store.service.AsyncConsistentMap;
 import org.onosproject.store.service.AsyncDocumentTree;
+import org.onosproject.store.service.AsyncConsistentMultimap;
+import org.onosproject.store.service.AsyncConsistentTreeMap;
 import org.onosproject.store.service.AtomicCounterBuilder;
 import org.onosproject.store.service.AtomicValueBuilder;
 import org.onosproject.store.service.ConsistentMap;
 import org.onosproject.store.service.ConsistentMapBuilder;
+import org.onosproject.store.service.ConsistentMultimapBuilder;
 import org.onosproject.store.service.ConsistentTreeMapBuilder;
 import org.onosproject.store.service.DistributedSetBuilder;
 import org.onosproject.store.service.DocumentTreeBuilder;
@@ -146,6 +149,13 @@
     }
 
     @Override
+    public <K, V> ConsistentMultimapBuilder<K, V> consistentMultimapBuilder() {
+        checkPermission(STORAGE_WRITE);
+        return new DefaultConsistentMultimapBuilder<K, V>(
+                federatedPrimitiveCreator);
+    }
+
+    @Override
     public <E> DistributedSetBuilder<E> setBuilder() {
         checkPermission(STORAGE_WRITE);
         return new DefaultDistributedSetBuilder<>(() -> this.<E, Boolean>consistentMapBuilder());
@@ -194,6 +204,22 @@
     }
 
     @Override
+    public <K, V> AsyncConsistentMultimap<K, V> getAsyncSetMultimap(
+            String name, Serializer serializer) {
+        checkPermission(STORAGE_WRITE);
+        return federatedPrimitiveCreator.newAsyncConsistentSetMultimap(name,
+                                                                serializer);
+    }
+
+    @Override
+    public <V> AsyncConsistentTreeMap<V> getAsyncTreeMap(
+            String name, Serializer serializer) {
+        checkPermission(STORAGE_WRITE);
+        return federatedPrimitiveCreator.newAsyncConsistentTreeMap(name,
+                                                                   serializer);
+    }
+
+    @Override
     public List<MapInfo> getMapInfo() {
         return listMapInfo(federatedPrimitiveCreator);
     }
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java
index b123ee0..b320a95 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java
@@ -39,6 +39,7 @@
 import org.onlab.util.HexString;
 import org.onosproject.store.primitives.DistributedPrimitiveCreator;
 import org.onosproject.store.primitives.resources.impl.AtomixConsistentMap;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimap;
 import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMap;
 import org.onosproject.store.primitives.resources.impl.AtomixCounter;
 import org.onosproject.store.primitives.resources.impl.AtomixDocumentTree;
@@ -48,6 +49,7 @@
 import org.onosproject.store.service.AsyncAtomicCounter;
 import org.onosproject.store.service.AsyncAtomicValue;
 import org.onosproject.store.service.AsyncConsistentMap;
+import org.onosproject.store.service.AsyncConsistentMultimap;
 import org.onosproject.store.service.AsyncConsistentTreeMap;
 import org.onosproject.store.service.AsyncDistributedSet;
 import org.onosproject.store.service.AsyncDocumentTree;
@@ -154,7 +156,7 @@
             atomixConsistentTreeMap.statusChangeListeners()
                     .forEach(listener -> listener.accept(mapper.apply(state)));
         };
-            resourceClient.client().onStateChange(statusListener);
+        resourceClient.client().onStateChange(statusListener);
         AsyncConsistentTreeMap<byte[]> rawMap =
                 new DelegatingAsyncConsistentTreeMap<byte[]>(atomixConsistentTreeMap) {
                     @Override
@@ -171,6 +173,38 @@
     }
 
     @Override
+    public <K, V> AsyncConsistentMultimap<K, V> newAsyncConsistentSetMultimap(
+            String name, Serializer serializer) {
+        AtomixConsistentSetMultimap atomixConsistentSetMultimap =
+                client.getResource(name, AtomixConsistentSetMultimap.class)
+                        .join();
+        Consumer<State> statusListener = state -> {
+            atomixConsistentSetMultimap.statusChangeListeners()
+                    .forEach(listener -> listener.accept(mapper.apply(state)));
+        };
+        resourceClient.client().onStateChange(statusListener);
+        AsyncConsistentMultimap<String, byte[]> rawMap =
+                new DelegatingAsyncConsistentMultimap<String, byte[]>(
+                        atomixConsistentSetMultimap) {
+                    @Override
+                    public String name() {
+                        return super.name();
+                    }
+                };
+        AsyncConsistentMultimap<K, V> trancodedMap =
+                DistributedPrimitives.<K, V, String, byte[]>newTranscodingMultimap(
+                        rawMap,
+                        key -> HexString.toHexString(serializer.encode(key)),
+                        string -> serializer.decode(
+                                HexString.fromHexString(string)),
+                        value -> value == null ? null :
+                                serializer.encode(value),
+                        bytes -> serializer.decode(bytes));
+        return trancodedMap;
+
+    }
+
+    @Override
     public <E> AsyncDistributedSet<E> newAsyncDistributedSet(String name, Serializer serializer) {
         return DistributedPrimitives.newSetFromMap(this.<E, Boolean>newAsyncConsistentMap(name, serializer));
     }
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/TranscodingAsyncConsistentMultimap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/TranscodingAsyncConsistentMultimap.java
new file mode 100644
index 0000000..e31653e
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/TranscodingAsyncConsistentMultimap.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.store.primitives.impl;
+
+import com.google.common.collect.ImmutableMultiset;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multiset;
+import org.onlab.util.Tools;
+import org.onosproject.store.service.AsyncConsistentMultimap;
+import org.onosproject.store.service.Versioned;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+/**
+ * An {@link AsyncConsistentMultimap} that maps its operation to operations to
+ * a differently typed {@link AsyncConsistentMultimap} by transcoding operation
+ * inputs and outputs while maintaining version numbers.
+ *
+ * @param <K2> key type of other map
+ * @param <V2> value type of other map
+ * @param <K1> key type of this map
+ * @param <V1> value type of this map
+ */
+public class TranscodingAsyncConsistentMultimap<K1, V1, K2, V2>
+        implements AsyncConsistentMultimap<K1, V1> {
+
+    private final AsyncConsistentMultimap<K2, V2> backingMap;
+    private final Function<K1, K2> keyEncoder;
+    private final Function<K2, K1> keyDecoder;
+    private final Function<V2, V1> valueDecoder;
+    private final Function<V1, V2> valueEncoder;
+    private final Function<? extends Versioned<V2>,
+            ? extends Versioned<V1>> versionedValueTransform;
+    private final Function<Versioned<Collection<? extends V2>>,
+            Versioned<Collection<? extends V1>>> versionedValueCollectionDecode;
+    private final Function<Collection<? extends V1>, Collection<V2>>
+            valueCollectionEncode;
+
+     public TranscodingAsyncConsistentMultimap(
+            AsyncConsistentMultimap<K2, V2> backingMap,
+            Function<K1, K2> keyEncoder,
+            Function<K2, K1> keyDecoder,
+            Function<V2, V1> valueDecoder,
+            Function<V1, V2> valueEncoder) {
+        this.backingMap = backingMap;
+        this.keyEncoder = k -> k == null ? null : keyEncoder.apply(k);
+        this.keyDecoder = k -> k == null ? null : keyDecoder.apply(k);
+        this.valueDecoder = v -> v == null ? null : valueDecoder.apply(v);
+        this.valueEncoder = v -> v == null ? null : valueEncoder.apply(v);
+        this.versionedValueTransform = v -> v == null ? null :
+                v.map(valueDecoder);
+        this.versionedValueCollectionDecode = v -> v == null ? null :
+                new Versioned<>(
+                        v.value()
+                                .stream()
+                                .map(valueDecoder)
+                                .collect(Collectors.toSet()),
+                        v.version(),
+                        v.creationTime());
+         this.valueCollectionEncode = v -> v == null ? null :
+                 v.stream().map(valueEncoder).collect(Collectors.toSet());
+    }
+
+    @Override
+    public CompletableFuture<Integer> size() {
+        return backingMap.size();
+    }
+
+    @Override
+    public CompletableFuture<Boolean> isEmpty() {
+        return backingMap.isEmpty();
+    }
+
+    @Override
+    public CompletableFuture<Boolean> containsKey(K1 key) {
+        try {
+            return backingMap.containsKey(keyEncoder.apply(key));
+        } catch (Exception e) {
+            return Tools.exceptionalFuture(e);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Boolean> containsValue(V1 value) {
+        try {
+            return backingMap.containsValue(valueEncoder.apply(value));
+        } catch (Exception e) {
+            return Tools.exceptionalFuture(e);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Boolean> containsEntry(K1 key, V1 value) {
+        try {
+            return backingMap.containsEntry(keyEncoder.apply(key),
+                                            valueEncoder.apply(value));
+        } catch (Exception e) {
+            return Tools.exceptionalFuture(e);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Boolean> put(K1 key, V1 value) {
+        try {
+            return backingMap.put(keyEncoder.apply(key),
+                                  valueEncoder.apply(value));
+        } catch (Exception e) {
+            return Tools.exceptionalFuture(e);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Boolean> remove(K1 key, V1 value) {
+        try {
+            return backingMap.remove(keyEncoder.apply(key), valueEncoder
+                    .apply(value));
+        } catch (Exception e) {
+            return Tools.exceptionalFuture(e);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Boolean> removeAll(
+            K1 key, Collection<? extends V1> values) {
+        try {
+            return backingMap.removeAll(
+                    keyEncoder.apply(key),
+                    values.stream().map(valueEncoder).collect(
+                            Collectors.toSet()));
+        } catch (Exception e) {
+            return Tools.exceptionalFuture(e);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Versioned<Collection<? extends V1>>>
+    removeAll(K1 key) {
+        try {
+            return backingMap.removeAll(keyEncoder.apply(key))
+                    .thenApply(versionedValueCollectionDecode);
+        } catch (Exception e) {
+            return Tools.exceptionalFuture(e);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Boolean>
+    putAll(K1 key, Collection<? extends V1> values) {
+        try {
+            return backingMap.putAll(keyEncoder.apply(key),
+                                     valueCollectionEncode.apply(values));
+        } catch (Exception e) {
+            return Tools.exceptionalFuture(e);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Versioned<Collection<? extends V1>>>
+    replaceValues(K1 key, Collection<V1> values) {
+        try {
+            return backingMap.replaceValues(keyEncoder.apply(key),
+                                     valueCollectionEncode.apply(values))
+                    .thenApply(versionedValueCollectionDecode);
+        } catch (Exception e) {
+            return Tools.exceptionalFuture(e);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Void> clear() {
+        return backingMap.clear();
+    }
+
+    @Override
+    public CompletableFuture<Versioned<Collection<? extends V1>>> get(K1 key) {
+        try {
+            return backingMap.get(keyEncoder.apply(key))
+                    .thenApply(versionedValueCollectionDecode);
+        } catch (Exception e) {
+            return Tools.exceptionalFuture(e);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Set<K1>> keySet() {
+        return backingMap.keySet().thenApply(s -> s.stream()
+                .map(keyDecoder)
+                .collect(Collectors.toSet()));
+    }
+
+    @Override
+    public CompletableFuture<Multiset<K1>> keys() {
+        return backingMap.keys().thenApply(s -> s.stream().map(keyDecoder)
+                .collect(new MultisetCollector<>()));
+    }
+
+    @Override
+    public CompletableFuture<Multiset<V1>> values() {
+        return backingMap.values().thenApply(s -> s.stream().map(valueDecoder)
+                .collect(new MultisetCollector<>()));
+    }
+
+    @Override
+    public CompletableFuture<Collection<Map.Entry<K1, V1>>> entries() {
+        return backingMap.entries().thenApply(s -> s.stream()
+        .map(e -> Maps.immutableEntry(keyDecoder.apply(e.getKey()),
+                                      valueDecoder.apply(e.getValue())))
+                .collect(Collectors.toSet()));
+    }
+
+    @Override
+    public CompletableFuture<Map<K1, Collection<V1>>> asMap() {
+        throw new UnsupportedOperationException("Unsupported operation.");
+    }
+
+    @Override
+    public String name() {
+        return backingMap.name();
+    }
+
+    @Override
+    public void addStatusChangeListener(Consumer<Status> listener) {
+        backingMap.addStatusChangeListener(listener);
+    }
+
+    @Override
+    public void removeStatusChangeListener(Consumer<Status> listener) {
+        backingMap.removeStatusChangeListener(listener);
+    }
+
+    @Override
+    public Collection<Consumer<Status>> statusChangeListeners() {
+        return backingMap.statusChangeListeners();
+    }
+
+    private class MultisetCollector<T> implements Collector<T,
+            ImmutableMultiset.Builder<T>,
+            Multiset<T>> {
+
+        @Override
+        public Supplier<ImmutableMultiset.Builder<T>> supplier() {
+            return ImmutableMultiset::builder;
+        }
+
+        @Override
+        public BiConsumer<ImmutableMultiset.Builder<T>, T> accumulator() {
+            return ((builder, t) -> builder.add(t));
+        }
+
+        @Override
+        public BinaryOperator<ImmutableMultiset.Builder<T>> combiner() {
+            return (a, b) -> {
+                a.addAll(b.build());
+                return a;
+            };
+        }
+
+        @Override
+        public Function<ImmutableMultiset.Builder<T>, Multiset<T>> finisher() {
+            return ImmutableMultiset.Builder::build;
+        }
+
+        @Override
+        public Set<Characteristics> characteristics() {
+            return EnumSet.of(Characteristics.UNORDERED);
+        }
+    }
+}
diff --git a/protocols/pcep/ctl/src/test/java/org/onosproject/pcelabelstore/util/StorageServiceAdapter.java b/protocols/pcep/ctl/src/test/java/org/onosproject/pcelabelstore/util/StorageServiceAdapter.java
index 4f60b06..b77faac 100644
--- a/protocols/pcep/ctl/src/test/java/org/onosproject/pcelabelstore/util/StorageServiceAdapter.java
+++ b/protocols/pcep/ctl/src/test/java/org/onosproject/pcelabelstore/util/StorageServiceAdapter.java
@@ -16,9 +16,12 @@
 package org.onosproject.pcelabelstore.util;
 
 import org.onosproject.store.service.AsyncDocumentTree;
+import org.onosproject.store.service.AsyncConsistentMultimap;
+import org.onosproject.store.service.AsyncConsistentTreeMap;
 import org.onosproject.store.service.AtomicCounterBuilder;
 import org.onosproject.store.service.AtomicValueBuilder;
 import org.onosproject.store.service.ConsistentMapBuilder;
+import org.onosproject.store.service.ConsistentMultimapBuilder;
 import org.onosproject.store.service.ConsistentTreeMapBuilder;
 import org.onosproject.store.service.DistributedSetBuilder;
 import org.onosproject.store.service.DocumentTreeBuilder;
@@ -80,6 +83,16 @@
     }
 
     @Override
+    public <K, V> AsyncConsistentMultimap<K, V> getAsyncSetMultimap(String name, Serializer serializer) {
+        return null;
+    }
+
+    @Override
+    public <V> AsyncConsistentTreeMap<V> getAsyncTreeMap(String name, Serializer serializer) {
+        return null;
+    }
+
+    @Override
     public <T> Topic<T> getTopic(String name, Serializer serializer) {
         // TODO Auto-generated method stub
         return null;
@@ -95,4 +108,7 @@
     public <V> AsyncDocumentTree<V> getDocumentTree(String name, Serializer serializer) {
         return null;
     }
+    public <K, V> ConsistentMultimapBuilder<K, V> consistentMultimapBuilder() {
+        return null;
+    }
 }
diff --git a/protocols/pcep/ctl/src/test/java/org/onosproject/pcelabelstore/util/TestStorageService.java b/protocols/pcep/ctl/src/test/java/org/onosproject/pcelabelstore/util/TestStorageService.java
index a7fbf1d..4f35be2 100644
--- a/protocols/pcep/ctl/src/test/java/org/onosproject/pcelabelstore/util/TestStorageService.java
+++ b/protocols/pcep/ctl/src/test/java/org/onosproject/pcelabelstore/util/TestStorageService.java
@@ -15,12 +15,20 @@
  */
 package org.onosproject.pcelabelstore.util;
 
+import org.onosproject.store.service.AsyncConsistentMultimap;
+import org.onosproject.store.service.AsyncConsistentTreeMap;
+import org.onosproject.store.service.AsyncDocumentTree;
 import org.onosproject.store.service.AtomicCounterBuilder;
 import org.onosproject.store.service.AtomicValueBuilder;
 import org.onosproject.store.service.ConsistentMapBuilder;
+import org.onosproject.store.service.ConsistentMultimapBuilder;
 import org.onosproject.store.service.DistributedSetBuilder;
 import org.onosproject.store.service.EventuallyConsistentMapBuilder;
+import org.onosproject.store.service.LeaderElectorBuilder;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.Topic;
 import org.onosproject.store.service.TransactionContextBuilder;
+import org.onosproject.store.service.WorkQueue;
 
 public class TestStorageService extends StorageServiceAdapter {
 
@@ -36,6 +44,11 @@
     }
 
     @Override
+    public <K, V> ConsistentMultimapBuilder<K, V> consistentMultimapBuilder() {
+        return null;
+    }
+
+    @Override
     public <E> DistributedSetBuilder<E> setBuilder() {
         return TestDistributedSet.builder();
     }
@@ -54,4 +67,34 @@
     public TransactionContextBuilder transactionContextBuilder() {
         throw new UnsupportedOperationException("transactionContextBuilder");
     }
+
+    @Override
+    public LeaderElectorBuilder leaderElectorBuilder() {
+        return null;
+    }
+
+    @Override
+    public <E> WorkQueue<E> getWorkQueue(String name, Serializer serializer) {
+        return null;
+    }
+
+    @Override
+    public <K, V> AsyncConsistentMultimap<K, V> getAsyncSetMultimap(String name, Serializer serializer) {
+        return null;
+    }
+
+    @Override
+    public <V> AsyncConsistentTreeMap<V> getAsyncTreeMap(String name, Serializer serializer) {
+        return null;
+    }
+
+    @Override
+    public <T> Topic<T> getTopic(String name, Serializer serializer) {
+        return null;
+    }
+
+    @Override
+    public <V> AsyncDocumentTree<V> getDocumentTree(String name, Serializer serializer) {
+        return null;
+    }
 }