Support for a java.util.Map implementation backed by ConsistentMap

Change-Id: I73240b69d0913f7534bd4006411ab217a40b254c
diff --git a/core/api/src/main/java/org/onosproject/store/service/ConsistentMap.java b/core/api/src/main/java/org/onosproject/store/service/ConsistentMap.java
index f1ef356..289da20 100644
--- a/core/api/src/main/java/org/onosproject/store/service/ConsistentMap.java
+++ b/core/api/src/main/java/org/onosproject/store/service/ConsistentMap.java
@@ -17,6 +17,7 @@
 package org.onosproject.store.service;
 
 import java.util.Collection;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.function.BiFunction;
@@ -281,4 +282,10 @@
      * @param listener listener to unregister
      */
     void removeListener(MapEventListener<K, V> listener);
-}
+
+    /**
+     * Returns a java.util.Map instance backed by this ConsistentMap.
+     * @return java.util.Map
+     */
+    Map<K, V> asJavaMap();
+}
\ No newline at end of file
diff --git a/core/api/src/test/java/org/onosproject/store/service/ConsistentMapAdapter.java b/core/api/src/test/java/org/onosproject/store/service/ConsistentMapAdapter.java
index e6df572..d0c1adf 100644
--- a/core/api/src/test/java/org/onosproject/store/service/ConsistentMapAdapter.java
+++ b/core/api/src/test/java/org/onosproject/store/service/ConsistentMapAdapter.java
@@ -141,4 +141,9 @@
     public void removeListener(MapEventListener<K, V> listener) {
 
     }
+
+    @Override
+    public Map<K, V> asJavaMap() {
+        return null;
+    }
 }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/ConsistentMapBackedJavaMap.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/ConsistentMapBackedJavaMap.java
new file mode 100644
index 0000000..58aca31
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/ConsistentMapBackedJavaMap.java
@@ -0,0 +1,145 @@
+package org.onosproject.store.consistent.impl;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Versioned;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Maps;
+
+/**
+ * Standard java Map backed by a ConsistentMap.
+ *
+ * @param <K> key type
+ * @param <V> value type
+ */
+public final class ConsistentMapBackedJavaMap<K, V> implements Map<K, V> {
+
+    private final ConsistentMap<K, V> backingMap;
+
+    public ConsistentMapBackedJavaMap(ConsistentMap<K, V> backingMap) {
+        this.backingMap = backingMap;
+    }
+
+    @Override
+    public int size() {
+        return backingMap.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return backingMap.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return backingMap.containsKey((K) key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return backingMap.containsValue((V) value);
+    }
+
+    @Override
+    public V get(Object key) {
+        return Versioned.valueOrElse(backingMap.get((K) key), null);
+    }
+
+    @Override
+    public V getOrDefault(Object key, V defaultValue) {
+        return Versioned.valueOrElse(backingMap.get((K) key), defaultValue);
+    }
+
+    @Override
+    public V put(K key, V value) {
+        return Versioned.valueOrElse(backingMap.put(key, value), null);
+    }
+
+    @Override
+    public V putIfAbsent(K key, V value) {
+        return Versioned.valueOrElse(backingMap.putIfAbsent(key, value), null);
+    }
+
+    @Override
+    public V remove(Object key) {
+        return Versioned.valueOrElse(backingMap.remove((K) key), null);
+    }
+
+    @Override
+    public boolean remove(Object key, Object value) {
+        return backingMap.remove((K) key, (V) value);
+    }
+
+    @Override
+    public V replace(K key, V value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean replace(K key, V oldValue, V newValue) {
+        return  backingMap.replace(key, oldValue, newValue);
+    }
+
+    @Override
+    public void putAll(Map<? extends K, ? extends V> m) {
+        m.forEach((k, v) -> {
+            backingMap.put(k, v);
+        });
+    }
+
+    @Override
+    public void clear() {
+        backingMap.clear();
+    }
+
+    @Override
+    public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        return Versioned.valueOrElse(backingMap.compute(key, remappingFunction), null);
+    }
+
+    @Override
+    public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
+        return Versioned.valueOrElse(backingMap.computeIfAbsent(key, mappingFunction), null);
+    }
+
+    @Override
+    public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        return Versioned.valueOrElse(backingMap.computeIfPresent(key, remappingFunction), null);
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return backingMap.keySet();
+    }
+
+    @Override
+    public Collection<V> values() {
+        return Collections2.transform(backingMap.values(), v -> v.value());
+    }
+
+    @Override
+    public Set<java.util.Map.Entry<K, V>> entrySet() {
+        return backingMap.entrySet()
+                         .stream()
+                         .map(entry -> Maps.immutableEntry(entry.getKey(), entry.getValue().value()))
+                         .collect(Collectors.toSet());
+    }
+
+    @Override
+    public void forEach(BiConsumer<? super K, ? super V> action) {
+        entrySet().forEach(e -> action.accept(e.getKey(), e.getValue()));
+    }
+
+    @Override
+    public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+        return computeIfPresent(key, (k, v) -> v == null ? value : remappingFunction.apply(v, value));
+    }
+}
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultConsistentMap.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultConsistentMap.java
index d6a657c..6f7b548 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultConsistentMap.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultConsistentMap.java
@@ -17,6 +17,7 @@
 package org.onosproject.store.consistent.impl;
 
 import java.util.Collection;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -44,6 +45,7 @@
     private static final int OPERATION_TIMEOUT_MILLIS = 5000;
 
     private final DefaultAsyncConsistentMap<K, V> asyncMap;
+    private Map<K, V> javaMap;
 
     public String name() {
         return asyncMap.name();
@@ -189,4 +191,14 @@
     public void removeListener(MapEventListener<K, V> listener) {
         asyncMap.addListener(listener);
     }
+
+    @Override
+    public Map<K, V> asJavaMap() {
+        synchronized (this) {
+            if (javaMap == null) {
+                javaMap = new ConsistentMapBackedJavaMap<>(this);
+            }
+        }
+        return javaMap;
+    }
 }
\ No newline at end of file