ONOS-2692: Implement methods to unregister resources

Change-Id: Iae88207c5edecf6645aeff3c15875178b5266634
diff --git a/core/api/src/main/java/org/onosproject/net/newresource/ResourceAdminService.java b/core/api/src/main/java/org/onosproject/net/newresource/ResourceAdminService.java
index 1a13c32..e94ee45 100644
--- a/core/api/src/main/java/org/onosproject/net/newresource/ResourceAdminService.java
+++ b/core/api/src/main/java/org/onosproject/net/newresource/ResourceAdminService.java
@@ -48,4 +48,28 @@
      * succeeds when each resource is not registered or unallocated.
      */
     <T> boolean registerResources(ResourcePath parent, List<T> children);
+
+    /**
+     * Unregister resources as the children of the parent resource path.
+     *
+     * @param parent parent resource path under which the resource are unregistered
+     * @param children resources to be unregistered as the children of the parent
+     * @param <T> type of resources
+     * @return true if unregistration is successfully done, false otherwise. Unregistration
+     * succeeds when each resource is not registered or unallocated.
+     */
+    default <T> boolean unregisterResources(ResourcePath parent, T... children) {
+        return unregisterResources(parent, Arrays.asList(children));
+    }
+
+    /**
+     * Unregister resources as the children of the parent resource path.
+     *
+     * @param parent parent resource path under which the resource are unregistered
+     * @param children resources to be unregistered as the children of the parent
+     * @param <T> type of resources
+     * @return true if unregistration is successfully done, false otherwise. Unregistration
+     * succeeds when each resource is not registered or unallocated.
+     */
+    <T> boolean unregisterResources(ResourcePath parent, List<T> children);
 }
diff --git a/core/api/src/main/java/org/onosproject/net/newresource/ResourceStore.java b/core/api/src/main/java/org/onosproject/net/newresource/ResourceStore.java
index b711f39..0189a57 100644
--- a/core/api/src/main/java/org/onosproject/net/newresource/ResourceStore.java
+++ b/core/api/src/main/java/org/onosproject/net/newresource/ResourceStore.java
@@ -25,6 +25,18 @@
     boolean register(ResourcePath parent, List<ResourcePath> children);
 
     /**
+     * Unregisters the resources as children of the parent resource in transactional way.
+     * The state after completion of this method is all the resources are unregistered,
+     * or no resource is unregistered. The whole unregistration fails when any one of the
+     * resource can't be unregistered.
+     *
+     * @param parent resource which is the parent of the resource to be unregistered
+     * @param children resources to be unregistered
+     * @return true if the registration succeeds, false otherwise
+     */
+    boolean unregister(ResourcePath parent, List<ResourcePath> children);
+
+    /**
      * Allocates the specified resources to the specified consumer in transactional way.
      * The state after completion of this method is all the resources are allocated to the consumer,
      * or no resource is allocated to the consumer. The whole allocation fails when any one of
diff --git a/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java b/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java
index f2a9286..abce072 100644
--- a/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java
+++ b/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java
@@ -130,4 +130,10 @@
         List<ResourcePath> resources = Lists.transform(children, x -> ResourcePath.child(parent, x));
         return store.register(parent, resources);
     }
+
+    @Override
+    public <T> boolean unregisterResources(ResourcePath parent, List<T> children) {
+        List<ResourcePath> resources = Lists.transform(children, x -> ResourcePath.child(parent, x));
+        return store.unregister(parent, resources);
+    }
 }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java b/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java
index a20c2b7..8728c5d 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java
@@ -121,6 +121,37 @@
     }
 
     @Override
+    public boolean unregister(ResourcePath resource, List<ResourcePath> children) {
+        checkNotNull(resource);
+        checkNotNull(children);
+
+        TransactionContext tx = service.transactionContextBuilder().build();
+        tx.begin();
+
+        try {
+            TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
+                    tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
+            TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
+                    tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
+
+            // even if one of the resources is allocated to a consumer,
+            // all unregistrations are regarded as failure
+            if (children.stream().anyMatch(x -> consumerTxMap.get(x) != null)) {
+                return abortTransaction(tx);
+            }
+
+            if (!removeValues(childTxMap, resource, children)) {
+                return abortTransaction(tx);
+            }
+
+            return commitTransaction(tx);
+        } catch (TransactionException e) {
+            log.error("Exception thrown, abort the transaction", e);
+            return abortTransaction(tx);
+        }
+    }
+
+    @Override
     public boolean allocate(List<ResourcePath> resources, ResourceConsumer consumer) {
         checkNotNull(resources);
         checkNotNull(consumer);
@@ -260,6 +291,30 @@
     }
 
     /**
+     * Removes teh values from the existing values associated with the specified key.
+     *
+     * @param map map holding multiple values for a key
+     * @param key key specifying values
+     * @param values values to be removed
+     * @param <K> type of the key
+     * @param <V> type of the element of the list
+     * @return true if the operation succeeds, false otherwise
+     */
+    private <K, V> boolean removeValues(TransactionalMap<K, List<V>> map, K key, List<V> values) {
+        List<V> oldValues = map.get(key);
+        List<V> newValues;
+        if (oldValues == null) {
+            newValues = new ArrayList<>();
+        } else {
+            LinkedHashSet<V> newSet = new LinkedHashSet<>(oldValues);
+            newSet.removeAll(values);
+            newValues = new ArrayList<>(newSet);
+        }
+
+        return map.replace(key, oldValues, newValues);
+    }
+
+    /**
      * Checks if the specified resource is registered as a child of a resource in the map.
      *
      * @param map map storing parent - child relationship of resources