Moving Source from connect point to HostId in MulticastHandling

Change-Id: Ie8f678e150b7ee388680b8d8f27df0bce60ec01f
diff --git a/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRoute.java b/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRoute.java
index 9d5afeb..7fe1a90 100644
--- a/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRoute.java
+++ b/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRoute.java
@@ -57,9 +57,10 @@
 
     /**
      * Creates the McastRoute object. The source Ip can be null if this route is intent for ASM.
+     *
      * @param source source Ip. Null if ASM route. Will translate in Optional.empty.
-     * @param group the multicast group
-     * @param type the route type.
+     * @param group  the multicast group
+     * @param type   the route type.
      */
     public McastRoute(IpAddress source, IpAddress group, Type type) {
         checkNotNull(group, "Multicast route must specify a group address");
diff --git a/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRouteData.java b/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRouteData.java
index e95c417..ce81ea9 100644
--- a/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRouteData.java
+++ b/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRouteData.java
@@ -17,7 +17,6 @@
 
 import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.HostId;
 
@@ -38,7 +37,7 @@
 @Beta
 public final class McastRouteData {
 
-    private final ConcurrentHashMap<ConnectPoint, Boolean> sources = new ConcurrentHashMap<>();
+    private final ConcurrentHashMap<HostId, Set<ConnectPoint>> sources = new ConcurrentHashMap<>();
     private final ConcurrentHashMap<HostId, Set<ConnectPoint>> sinks = new ConcurrentHashMap<>();
 
     private McastRouteData() {
@@ -49,13 +48,41 @@
      *
      * @return set of sources
      */
-    public Set<ConnectPoint> sources() {
-        return ImmutableSet.copyOf(sources.keySet());
+    public Map<HostId, Set<ConnectPoint>> sources() {
+        return ImmutableMap.copyOf(sources);
     }
 
     /**
      * Sources contained in the associated route.
      *
+     * @return map of hostIds and associated sources
+     */
+    public Set<ConnectPoint> allSources() {
+        return sources.values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
+    }
+
+    /**
+     * Sources contained in the associated route for the given host.
+     *
+     * @param hostId the host
+     * @return set of sources
+     */
+    public Set<ConnectPoint> sources(HostId hostId) {
+        return sources.get(hostId);
+    }
+
+    /**
+     * Sources contained in the associated route that are not bound to any host.
+     *
+     * @return set of sources
+     */
+    public Set<ConnectPoint> nonHostSources() {
+        return sources.get(HostId.NONE);
+    }
+
+    /**
+     * Sinks contained in the associated route.
+     *
      * @return map of hostIds and associated sinks
      */
     public Map<HostId, Set<ConnectPoint>> sinks() {
@@ -91,13 +118,34 @@
     }
 
     /**
-     * Add the sources for the associated route.
+     * Adds sources for a given host Id. If the Host Id is {@link HostId#NONE} the sources are intended to be
+     * used at all times independently of the attached host.
      *
-     * @param sources set of sources
+     * @param hostId the host
+     * @param sources  the sources
      */
-    public void addSources(Set<ConnectPoint> sources) {
+    public void addSources(HostId hostId, Set<ConnectPoint> sources) {
+        checkNotNull(hostId);
         checkArgument(!sources.contains(null));
-        sources.forEach(source -> this.sources.put(source, true));
+        //if existing we add to current set, otherwise we put them all
+        this.sources.compute(hostId, (host, existingSources) -> {
+            if (existingSources != null) {
+                existingSources.addAll(sources);
+                return existingSources;
+            } else {
+                return sources;
+            }
+        });
+    }
+
+    /**
+     * Adds sources for this route that are not associated directly with a given host.
+     *
+     * @param sources the sources
+     */
+    public void addNonHostSources(Set<ConnectPoint> sources) {
+        checkArgument(!sources.contains(null));
+        addSources(HostId.NONE, sources);
     }
 
     /**
@@ -108,13 +156,31 @@
     }
 
     /**
-     * Removes the given sources contained in the associated route.
+     * Removes the given source contained in the associated route.
      *
+     * @param source the source to remove
+     */
+    public void removeSource(HostId source) {
+        checkNotNull(source);
+        sources.remove(source);
+    }
+
+    /**
+     * Removes all the given sources for the given host for this route.
+     *
+     * @param hostId  the host
      * @param sources the sources to remove
      */
-    public void removeSources(Set<ConnectPoint> sources) {
+    public void removeSources(HostId hostId, Set<ConnectPoint> sources) {
+        checkNotNull(hostId);
         checkArgument(!sources.contains(null));
-        sources.forEach(this.sources::remove);
+        //if existing we remove from current set, otherwise just skip them
+        this.sources.compute(hostId, (host, existingSources) -> {
+            if (existingSources != null) {
+                existingSources.removeAll(sources);
+            }
+            return existingSources;
+        });
     }
 
     /**
@@ -128,11 +194,14 @@
         checkNotNull(hostId);
         checkArgument(!sinks.contains(null));
         //if existing we add to current set, otherwise we put them all
-        if (this.sinks.containsKey(hostId)) {
-            this.sinks.get(hostId).addAll(sinks);
-        } else {
-            this.sinks.put(hostId, sinks);
-        }
+        this.sinks.compute(hostId, (host, existingSinks) -> {
+            if (existingSinks != null) {
+                existingSinks.addAll(sinks);
+                return existingSinks;
+            } else {
+                return sinks;
+            }
+        });
     }
 
     /**
@@ -142,7 +211,7 @@
      */
     public void addNonHostSinks(Set<ConnectPoint> sinks) {
         checkArgument(!sinks.contains(null));
-        this.sinks.put(HostId.NONE, sinks);
+        this.addSinks(HostId.NONE, sinks);
     }
 
     /**
@@ -172,9 +241,12 @@
         checkNotNull(hostId);
         checkArgument(!sinks.contains(null));
         //if existing we remove from current set, otherwise just skip them
-        if (this.sinks.containsKey(hostId)) {
-            this.sinks.get(hostId).removeAll(sinks);
-        }
+        this.sinks.compute(hostId, (host, existingSinks) -> {
+            if (existingSinks != null) {
+                existingSinks.removeAll(sinks);
+            }
+            return existingSinks;
+        });
     }
 
     /**
diff --git a/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRouteUpdate.java b/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRouteUpdate.java
index 4333465..a117cc4 100644
--- a/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRouteUpdate.java
+++ b/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastRouteUpdate.java
@@ -37,12 +37,14 @@
     private static final String SINK_NOT_NULL = "Sink cannot be null";
 
     private final McastRoute route;
-    private final Set<ConnectPoint> sources;
+    private final Map<HostId, Set<ConnectPoint>> sources;
     private final Map<HostId, Set<ConnectPoint>> sinks;
 
-    private McastRouteUpdate(McastRoute route, Set<ConnectPoint> source, Map<HostId, Set<ConnectPoint>> sinks) {
+    private McastRouteUpdate(McastRoute route,
+                             Map<HostId, Set<ConnectPoint>> sources,
+                             Map<HostId, Set<ConnectPoint>> sinks) {
         this.route = checkNotNull(route, ROUTE_NOT_NULL);
-        this.sources = checkNotNull(source, SOURCE_NOT_NULL);
+        this.sources = checkNotNull(sources, SOURCE_NOT_NULL);
         this.sinks = checkNotNull(sinks, SINK_NOT_NULL);
     }
 
@@ -55,7 +57,7 @@
      * @return the McastRouteUpdate object.
      */
     public static McastRouteUpdate mcastRouteUpdate(McastRoute route,
-                                                    Set<ConnectPoint> sources,
+                                                    Map<HostId, Set<ConnectPoint>> sources,
                                                     Map<HostId, Set<ConnectPoint>> sinks) {
         return new McastRouteUpdate(route, sources, sinks);
     }
@@ -72,9 +74,9 @@
     /**
      * The sources.
      *
-     * @return an optional connect point
+     * @return a set of connect points
      */
-    public Set<ConnectPoint> sources() {
+    public Map<HostId, Set<ConnectPoint>> sources() {
         return sources;
     }
 
diff --git a/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastStore.java b/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastStore.java
index aa429b2..484f32ec 100644
--- a/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastStore.java
+++ b/apps/mcast/api/src/main/java/org/onosproject/mcast/api/McastStore.java
@@ -43,10 +43,22 @@
     void removeRoute(McastRoute route);
 
     /**
-     * Add to the store with source information for the given route.
+     * Updates the store with a host based source information for a given route. There may be
+     * multiple source connect points for the given host.
      *
-     * @param route   a Multicast route
-     * @param sources a set of sources
+     * @param route         a Multicast route
+     * @param hostId        the host source
+     * @param connectPoints the sources connect point
+     */
+    void storeSource(McastRoute route, HostId hostId, Set<ConnectPoint> connectPoints);
+
+    /**
+     * Updates the store with source information for a given route.
+     * The source stored with this method are not tied with any host.
+     * Traffic will be sent from all of them.
+     *
+     * @param route a Multicast route
+     * @param sources set of specific connect points
      */
     void storeSources(McastRoute route, Set<ConnectPoint> sources);
 
@@ -59,12 +71,20 @@
 
     /**
      * Removes from the store the source information for the given route.
-     * value.
      *
-     * @param route   a Multicast route
-     * @param sources a set of sources
+     * @param route  a Multicast route
+     * @param source a source
      */
-    void removeSources(McastRoute route, Set<ConnectPoint> sources);
+    void removeSource(McastRoute route, HostId source);
+
+    /**
+     * Removes a set of source connect points for a given host the route.
+     *
+     * @param route         the multicast route
+     * @param hostId        a source host
+     * @param connectPoints a given set of connect points to remove
+     */
+    void removeSources(McastRoute route, HostId hostId, Set<ConnectPoint> connectPoints);
 
     /**
      * Updates the store with a host based sink information for a given route. There may be
@@ -127,6 +147,15 @@
     Set<ConnectPoint> sourcesFor(McastRoute route);
 
     /**
+     * Obtains the sources for a given host for a given Multicast route.
+     *
+     * @param route  a Multicast route
+     * @param hostId the host
+     * @return a set of sources
+     */
+    Set<ConnectPoint> sourcesFor(McastRoute route, HostId hostId);
+
+    /**
      * Obtains the sinks for a Multicast route.
      *
      * @param route a Multicast route
diff --git a/apps/mcast/api/src/main/java/org/onosproject/mcast/api/MulticastRouteService.java b/apps/mcast/api/src/main/java/org/onosproject/mcast/api/MulticastRouteService.java
index bcee070..bd32363 100644
--- a/apps/mcast/api/src/main/java/org/onosproject/mcast/api/MulticastRouteService.java
+++ b/apps/mcast/api/src/main/java/org/onosproject/mcast/api/MulticastRouteService.java
@@ -61,11 +61,33 @@
     Set<McastRoute> getRoute(IpAddress groupIp, IpAddress sourceIp);
 
     /**
-     * Adds a set of sources connect points to the route from where the
+     * Adds a host as a source to the route from where the
      * data stream is originating.
      *
-     * @param route   the Multicast route
-     * @param sources a set of sources
+     * @param route  the Multicast route
+     * @param source a source host
+     */
+    void addSource(McastRoute route, HostId source);
+
+
+    /**
+     * Adds a set of source connect points for a given host source to the route to
+     * which a data stream should be sent to.
+     *
+     * @param route         a Multicast route
+     * @param hostId        a source host
+     * @param connectPoints the source for the specific host
+     */
+    void addSources(McastRoute route, HostId hostId, Set<ConnectPoint> connectPoints);
+
+    /**
+     * Adds a set of sources to the route from which a data stream should be
+     * sent to. If this method is used the connect points will all be
+     * used as different sources for that Mcast Tree. For dual-homed sources
+     * please use {@link #addSource(McastRoute route, HostId hostId) addSource}.
+     *
+     * @param route a Multicast route
+     * @param sources a set of source connect points
      */
     void addSources(McastRoute route, Set<ConnectPoint> sources);
 
@@ -77,12 +99,12 @@
     void removeSources(McastRoute route);
 
     /**
-     * Removes a set of sources connect points from the route.
+     * Removes a source host from the route.
      *
-     * @param route   the Multicast route
-     * @param sources a set of sources
+     * @param route  the Multicast route
+     * @param source a host source
      */
-    void removeSources(McastRoute route, Set<ConnectPoint> sources);
+    void removeSource(McastRoute route, HostId source);
 
     /**
      * Adds a sink to the route to which a data stream should be
@@ -112,7 +134,7 @@
      * @param route a Multicast route
      * @param sinks a set of sink connect point
      */
-    void addSink(McastRoute route, Set<ConnectPoint> sinks);
+    void addSinks(McastRoute route, Set<ConnectPoint> sinks);
 
     /**
      * Removes all the sinks from the route.
@@ -157,6 +179,15 @@
     Set<ConnectPoint> sources(McastRoute route);
 
     /**
+     * Find the set of connect points for a given source for this route.
+     *
+     * @param route  a Multicast route
+     * @param hostId the host
+     * @return a list of connect points
+     */
+    Set<ConnectPoint> sources(McastRoute route, HostId hostId);
+
+    /**
      * Find the list of sinks for this route.
      *
      * @param route a Multicast route