[AETHER-72] Refactoring RouteService

- to use bulk updates interface
- to use new getRoutesForNextHops API
- to use multi-thread resolver
- to use multi-thread hostexec
- to use a concurrent hashmap instead of synchronized
- to use a non-blocking resolved store

Additionally updates unit tests

Change-Id: Id960abd0f2a1b03066ce34b6a2f72b76566bb58c
diff --git a/apps/route-service/app/src/main/java/org/onosproject/routeservice/store/DistributedRouteStore.java b/apps/route-service/app/src/main/java/org/onosproject/routeservice/store/DistributedRouteStore.java
index efdb792..f2661a1 100644
--- a/apps/route-service/app/src/main/java/org/onosproject/routeservice/store/DistributedRouteStore.java
+++ b/apps/route-service/app/src/main/java/org/onosproject/routeservice/store/DistributedRouteStore.java
@@ -17,6 +17,7 @@
 package org.onosproject.routeservice.store;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.util.KryoNamespace;
@@ -37,11 +38,13 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
 
 import static org.onlab.util.Tools.groupedThreads;
 
@@ -116,11 +119,27 @@
     }
 
     @Override
+    public void updateRoutes(Collection<Route> routes) {
+        Map<RouteTableId, Set<Route>> computedTables = computeRouteTablesFromRoutes(routes);
+        computedTables.forEach(
+                ((routeTableId, routesToAdd) -> getDefaultRouteTable(routeTableId).update(routesToAdd))
+        );
+    }
+
+    @Override
     public void removeRoute(Route route) {
         getDefaultRouteTable(route).remove(route);
     }
 
     @Override
+    public void removeRoutes(Collection<Route> routes) {
+        Map<RouteTableId, Set<Route>> computedTables = computeRouteTablesFromRoutes(routes);
+        computedTables.forEach(
+                ((routeTableId, routesToRemove) -> getDefaultRouteTable(routeTableId).remove(routesToRemove))
+        );
+    }
+
+    @Override
     public void replaceRoute(Route route) {
         getDefaultRouteTable(route).replace(route);
     }
@@ -146,6 +165,15 @@
     }
 
     @Override
+    public Collection<RouteSet> getRoutesForNextHops(Collection<IpAddress> nextHops) {
+        Map<RouteTableId, Set<IpAddress>> computedTables = computeRouteTablesFromIps(nextHops);
+        return computedTables.entrySet().stream()
+                .map(entry -> getDefaultRouteTable(entry.getKey()).getRoutesForNextHops(entry.getValue()))
+                .flatMap(Collection::stream)
+                .collect(Collectors.toList());
+    }
+
+    @Override
     public RouteSet getRoutes(IpPrefix prefix) {
         return getDefaultRouteTable(prefix.address()).getRoutes(prefix);
     }
@@ -170,6 +198,30 @@
         return routeTables.getOrDefault(routeTableId, EmptyRouteTable.instance());
     }
 
+    private RouteTable getDefaultRouteTable(RouteTableId routeTableId) {
+        return routeTables.getOrDefault(routeTableId, EmptyRouteTable.instance());
+    }
+
+    private Map<RouteTableId, Set<Route>> computeRouteTablesFromRoutes(Collection<Route> routes) {
+        Map<RouteTableId, Set<Route>> computedTables = new HashMap<>();
+        routes.forEach(route -> {
+            RouteTableId routeTableId = (route.prefix().address().isIp4()) ? IPV4 : IPV6;
+            Set<Route> tempRoutes = computedTables.computeIfAbsent(routeTableId, k -> Sets.newHashSet());
+            tempRoutes.add(route);
+        });
+        return computedTables;
+    }
+
+    private Map<RouteTableId, Set<IpAddress>> computeRouteTablesFromIps(Collection<IpAddress> ipAddresses) {
+        Map<RouteTableId, Set<IpAddress>> computedTables = new HashMap<>();
+        ipAddresses.forEach(ipAddress -> {
+            RouteTableId routeTableId = (ipAddress.isIp4()) ? IPV4 : IPV6;
+            Set<IpAddress> tempIpAddresses = computedTables.computeIfAbsent(routeTableId, k -> Sets.newHashSet());
+            tempIpAddresses.add(ipAddress);
+        });
+        return computedTables;
+    }
+
     private class InternalRouteStoreDelegate implements RouteStoreDelegate {
         @Override
         public void notify(InternalRouteEvent event) {