Support for building a immutable ConsistentMap and DistributedSet

Change-Id: Ic34684551f5c7d1f4fdc4cd3fc1a7bfabc5681f5
diff --git a/core/api/src/main/java/org/onosproject/store/service/ConsistentMapBuilder.java b/core/api/src/main/java/org/onosproject/store/service/ConsistentMapBuilder.java
index 6a02f34..6e3fba4 100644
--- a/core/api/src/main/java/org/onosproject/store/service/ConsistentMapBuilder.java
+++ b/core/api/src/main/java/org/onosproject/store/service/ConsistentMapBuilder.java
@@ -56,6 +56,15 @@
     public ConsistentMapBuilder<K, V> withPartitionsDisabled();
 
     /**
+     * Disables map updates.
+     * <p>
+     * Attempt to update the built map will throw {@code UnsupportedOperationException}.
+     *
+     * @return this ConsistentMapBuilder
+     */
+    public ConsistentMapBuilder<K, V> withUpdatesDisabled();
+
+    /**
      * Builds an consistent map based on the configuration options
      * supplied to this builder.
      *
diff --git a/core/api/src/main/java/org/onosproject/store/service/SetBuilder.java b/core/api/src/main/java/org/onosproject/store/service/SetBuilder.java
index cd2e060..3ea7aaf 100644
--- a/core/api/src/main/java/org/onosproject/store/service/SetBuilder.java
+++ b/core/api/src/main/java/org/onosproject/store/service/SetBuilder.java
@@ -53,6 +53,15 @@
     public SetBuilder<E> withSerializer(Serializer serializer);
 
     /**
+     * Disables set updates.
+     * <p>
+     * Attempt to update the built set will throw {@code UnsupportedOperationException}.
+     *
+     * @return this SetBuilder
+     */
+    SetBuilder<E> withUpdatesDisabled();
+
+    /**
      * Builds an set based on the configuration options
      * supplied to this builder.
      *
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMap.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMap.java
index 09f612e..dd80db6 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMap.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncConsistentMap.java
@@ -48,6 +48,7 @@
     private final String name;
     private final Database database;
     private final Serializer serializer;
+    private final boolean readOnly;
 
     private static final String ERROR_NULL_KEY = "Key cannot be null";
     private static final String ERROR_NULL_VALUE = "Null values are not allowed";
@@ -68,10 +69,12 @@
 
     public DefaultAsyncConsistentMap(String name,
             Database database,
-            Serializer serializer) {
+            Serializer serializer,
+            boolean readOnly) {
         this.name = checkNotNull(name, "map name cannot be null");
         this.database = checkNotNull(database, "database cannot be null");
         this.serializer = checkNotNull(serializer, "serializer cannot be null");
+        this.readOnly = readOnly;
     }
 
     @Override
@@ -108,6 +111,7 @@
     public CompletableFuture<Versioned<V>> put(K key, V value) {
         checkNotNull(key, ERROR_NULL_KEY);
         checkNotNull(value, ERROR_NULL_VALUE);
+        checkIfUnmodifiable();
         return database.put(name, keyCache.getUnchecked(key), serializer.encode(value))
                 .thenApply(this::unwrapResult)
                 .thenApply(v -> v != null
@@ -117,6 +121,7 @@
     @Override
     public CompletableFuture<Versioned<V>> remove(K key) {
         checkNotNull(key, ERROR_NULL_KEY);
+        checkIfUnmodifiable();
         return database.remove(name, keyCache.getUnchecked(key))
                 .thenApply(this::unwrapResult)
                 .thenApply(v -> v != null
@@ -125,6 +130,7 @@
 
     @Override
     public CompletableFuture<Void> clear() {
+        checkIfUnmodifiable();
         return database.clear(name).thenApply(this::unwrapResult);
     }
 
@@ -157,6 +163,7 @@
     public CompletableFuture<Versioned<V>> putIfAbsent(K key, V value) {
         checkNotNull(key, ERROR_NULL_KEY);
         checkNotNull(value, ERROR_NULL_VALUE);
+        checkIfUnmodifiable();
         return database.putIfAbsent(name,
                                     keyCache.getUnchecked(key),
                                     serializer.encode(value))
@@ -169,6 +176,7 @@
     public CompletableFuture<Boolean> remove(K key, V value) {
         checkNotNull(key, ERROR_NULL_KEY);
         checkNotNull(value, ERROR_NULL_VALUE);
+        checkIfUnmodifiable();
         return database.remove(name, keyCache.getUnchecked(key), serializer.encode(value))
                 .thenApply(this::unwrapResult);
     }
@@ -176,6 +184,7 @@
     @Override
     public CompletableFuture<Boolean> remove(K key, long version) {
         checkNotNull(key, ERROR_NULL_KEY);
+        checkIfUnmodifiable();
         return database.remove(name, keyCache.getUnchecked(key), version)
                 .thenApply(this::unwrapResult);
 
@@ -185,6 +194,7 @@
     public CompletableFuture<Boolean> replace(K key, V oldValue, V newValue) {
         checkNotNull(key, ERROR_NULL_KEY);
         checkNotNull(newValue, ERROR_NULL_VALUE);
+        checkIfUnmodifiable();
         byte[] existing = oldValue != null ? serializer.encode(oldValue) : null;
         return database.replace(name, keyCache.getUnchecked(key), existing, serializer.encode(newValue))
                 .thenApply(this::unwrapResult);
@@ -194,6 +204,7 @@
     public CompletableFuture<Boolean> replace(K key, long oldVersion, V newValue) {
         checkNotNull(key, ERROR_NULL_KEY);
         checkNotNull(newValue, ERROR_NULL_VALUE);
+        checkIfUnmodifiable();
         return database.replace(name, keyCache.getUnchecked(key), oldVersion, serializer.encode(newValue))
                 .thenApply(this::unwrapResult);
     }
@@ -216,4 +227,10 @@
             throw new IllegalStateException("Must not be here");
         }
     }
+
+    private void checkIfUnmodifiable() {
+        if (readOnly) {
+            throw new UnsupportedOperationException();
+        }
+    }
 }
\ 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 046aafb..7b0ad10 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
@@ -45,8 +45,9 @@
 
     public DefaultConsistentMap(String name,
             Database database,
-            Serializer serializer) {
-        asyncMap = new DefaultAsyncConsistentMap<>(name, database, serializer);
+            Serializer serializer,
+            boolean readOnly) {
+        asyncMap = new DefaultAsyncConsistentMap<>(name, database, serializer, readOnly);
     }
 
     @Override
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultConsistentMapBuilder.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultConsistentMapBuilder.java
index ba56e59..534b59f 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultConsistentMapBuilder.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultConsistentMapBuilder.java
@@ -19,6 +19,7 @@
     private Serializer serializer;
     private String name;
     private boolean partitionsEnabled = true;
+    private boolean readOnly = false;
     private final Database partitionedDatabase;
     private final Database inMemoryDatabase;
 
@@ -47,6 +48,12 @@
         return this;
     }
 
+    @Override
+    public ConsistentMapBuilder<K, V> withUpdatesDisabled() {
+        readOnly = true;
+        return this;
+    }
+
     private boolean validInputs() {
         return name != null && serializer != null;
     }
@@ -57,7 +64,8 @@
         return new DefaultConsistentMap<>(
                 name,
                 partitionsEnabled ? partitionedDatabase : inMemoryDatabase,
-                serializer);
+                serializer,
+                readOnly);
     }
 
     @Override
@@ -66,6 +74,7 @@
         return new DefaultAsyncConsistentMap<>(
                 name,
                 partitionsEnabled ? partitionedDatabase : inMemoryDatabase,
-                serializer);
+                serializer,
+                readOnly);
     }
 }
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDistributedSet.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDistributedSet.java
index 97d5ef3..adc9dcd 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDistributedSet.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDistributedSet.java
@@ -33,8 +33,8 @@
 
     private final ConsistentMap<E, Boolean> backingMap;
 
-    public DefaultDistributedSet(String name, Database database, Serializer serializer) {
-        backingMap = new DefaultConsistentMap<>(name, database, serializer);
+    public DefaultDistributedSet(String name, Database database, Serializer serializer, boolean readOnly) {
+        backingMap = new DefaultConsistentMap<>(name, database, serializer, readOnly);
     }
 
     @Override
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultSetBuilder.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultSetBuilder.java
index dfc9c40..566953f 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultSetBuilder.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultSetBuilder.java
@@ -34,6 +34,7 @@
     private Serializer serializer;
     private String name;
     private final Database database;
+    private boolean readOnly;
 
     public DefaultSetBuilder(Database database) {
         this.database = checkNotNull(database);
@@ -53,6 +54,12 @@
         return this;
     }
 
+    @Override
+    public SetBuilder<E> withUpdatesDisabled() {
+        readOnly = true;
+        return this;
+    }
+
     private boolean validInputs() {
         return name != null && serializer != null;
     }
@@ -60,6 +67,6 @@
     @Override
     public Set<E> build() {
         checkState(validInputs());
-        return new DefaultDistributedSet<>(name, database, serializer);
+        return new DefaultDistributedSet<>(name, database, serializer, readOnly);
     }
 }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultTransactionContext.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultTransactionContext.java
index 10b7cfd..5570e30 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultTransactionContext.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultTransactionContext.java
@@ -72,7 +72,7 @@
         checkNotNull(serializer);
         return txMaps.computeIfAbsent(mapName, name -> new DefaultTransactionalMap<>(
                                 name,
-                                new DefaultConsistentMap<>(name, database, serializer),
+                                new DefaultConsistentMap<>(name, database, serializer, false),
                                 this,
                                 serializer));
     }
@@ -99,4 +99,4 @@
         checkState(isOpen, TX_NOT_OPEN_ERROR);
         txMaps.values().forEach(m -> m.rollback());
     }
-}
\ No newline at end of file
+}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/TransactionManager.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/TransactionManager.java
index 45b590a..d8e9930 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/TransactionManager.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/TransactionManager.java
@@ -60,7 +60,7 @@
      */
     public TransactionManager(Database database) {
         this.database = checkNotNull(database, "database cannot be null");
-        this.transactions = new DefaultAsyncConsistentMap<>("onos-transactions", this.database, serializer);
+        this.transactions = new DefaultAsyncConsistentMap<>("onos-transactions", this.database, serializer, false);
     }
 
     /**
@@ -121,4 +121,4 @@
                             transaction.transition(Transaction.State.ROLLEDBACK)))
                 .thenApply(v -> true);
     }
-}
\ No newline at end of file
+}