[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/DefaultRouteTable.java b/apps/route-service/app/src/main/java/org/onosproject/routeservice/store/DefaultRouteTable.java
index f052f04..c24f005 100644
--- a/apps/route-service/app/src/main/java/org/onosproject/routeservice/store/DefaultRouteTable.java
+++ b/apps/route-service/app/src/main/java/org/onosproject/routeservice/store/DefaultRouteTable.java
@@ -18,6 +18,7 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ExecutorService;
@@ -43,6 +44,7 @@
 import org.onosproject.store.service.StorageService;
 import org.onosproject.store.service.Versioned;
 
+
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -133,6 +135,14 @@
     }
 
     @Override
+    public void update(Collection<Route> routesAdded) {
+        Map<String, Collection<? extends RawRoute>> computedRoutes = new HashMap<>();
+        computeRoutesToAdd(routesAdded).forEach((prefix, routes) -> computedRoutes.computeIfAbsent(
+                prefix, k -> Sets.newHashSet(routes)));
+        routes.putAll(computedRoutes);
+    }
+
+    @Override
     public void remove(Route route) {
         getRoutes(route.prefix())
             .routes()
@@ -145,6 +155,14 @@
     }
 
     @Override
+    public void remove(Collection<Route> routesRemoved) {
+        Map<String, Collection<? extends RawRoute>> computedRoutes = new HashMap<>();
+        computeRoutesToRemove(routesRemoved).forEach((prefix, routes) -> computedRoutes.computeIfAbsent(
+                prefix, k -> Sets.newHashSet(routes)));
+        routes.removeAll(computedRoutes);
+    }
+
+    @Override
     public void replace(Route route) {
         routes.replaceValues(route.prefix().toString(), Sets.newHashSet(new RawRoute(route)));
     }
@@ -180,6 +198,53 @@
             .collect(Collectors.toSet());
     }
 
+    @Override
+    public Collection<RouteSet> getRoutesForNextHops(Collection<IpAddress> nextHops) {
+        // First create a reduced snapshot of the store iterating one time the map
+        Map<String, Collection<? extends RawRoute>> filteredRouteStore = new HashMap<>();
+        routes.values().stream()
+                .filter(r -> nextHops.contains(IpAddress.valueOf(r.nextHop())))
+                .forEach(r -> filteredRouteStore.computeIfAbsent(r.prefix, k -> {
+                    // We need to get all the routes because the resolve logic
+                    // will use the alternatives as well
+                    Versioned<Collection<? extends RawRoute>> routeSet = routes.get(k);
+                    if (routeSet != null) {
+                        return routeSet.value();
+                    }
+                    return null;
+                }));
+        // Return the collection of the routeSet we have to resolve
+        return filteredRouteStore.entrySet().stream()
+                .map(entry -> new RouteSet(id, IpPrefix.valueOf(entry.getKey()),
+                                           entry.getValue().stream().map(RawRoute::route).collect(Collectors.toSet())))
+                .collect(Collectors.toSet());
+    }
+
+    private Map<String, Collection<RawRoute>> computeRoutesToAdd(Collection<Route> routesAdded) {
+        Map<String, Collection<RawRoute>> computedRoutes = new HashMap<>();
+        routesAdded.forEach(route -> {
+            Collection<RawRoute> tempRoutes = computedRoutes.computeIfAbsent(
+                    route.prefix().toString(), k -> Sets.newHashSet());
+            tempRoutes.add(new RawRoute(route));
+        });
+        return computedRoutes;
+    }
+
+    private Map<String, Collection<RawRoute>> computeRoutesToRemove(Collection<Route> routesRemoved) {
+        Map<String, Collection<RawRoute>> computedRoutes = new HashMap<>();
+        routesRemoved.forEach(route -> getRoutes(route.prefix())
+                .routes()
+                .stream()
+                .filter(r -> r.equals(route))
+                .findAny()
+                .ifPresent(matchRoute -> {
+                    Collection<RawRoute> tempRoutes = computedRoutes.computeIfAbsent(
+                            matchRoute.prefix().toString(), k -> Sets.newHashSet());
+                    tempRoutes.add(new RawRoute(matchRoute));
+                }));
+        return computedRoutes;
+    }
+
     private class RouteTableListener
             implements MultimapEventListener<String, RawRoute> {