updated mcastforwarding to handle new events

Change-Id: I8b3e9acc147e1f25f19ac25e579f65353cf0c72e
diff --git a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java
index 60851d6..f8856e6 100644
--- a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java
+++ b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java
@@ -32,7 +32,13 @@
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
 import org.onosproject.net.mcast.McastRoute;
+import org.onosproject.net.mcast.McastListener;
+import org.onosproject.net.mcast.McastEvent;
 import org.onosproject.net.mcast.MulticastRouteService;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.InboundPacket;
@@ -44,6 +50,10 @@
 import org.slf4j.Logger;
 
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -66,9 +76,12 @@
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    private IntentService intentService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MulticastRouteService mcastRouteManager;
 
-
+    protected McastIntentManager mcastIntentManager;
 
     private ReactivePacketProcessor processor = new ReactivePacketProcessor();
     private static ApplicationId appId;
@@ -80,6 +93,9 @@
     public void activate() {
         appId = coreService.registerApplication("org.onosproject.mfwd");
 
+        mcastIntentManager = new McastIntentManager();
+        mcastRouteManager.addListener(mcastIntentManager);
+
         packetService.addProcessor(processor, PacketProcessor.director(2));
 
         // Build a traffic selector for all multicast traffic
@@ -98,6 +114,8 @@
     @Deactivate
     public void deactivate() {
         packetService.removeProcessor(processor);
+        mcastRouteManager.removeListener(mcastIntentManager);
+        mcastIntentManager.withdrawAllIntents();
         processor = null;
         log.info("Stopped");
     }
@@ -112,10 +130,47 @@
     }
 
     /**
+     * Forward the packet to it's multicast destinations.
+     *
+     * @param context The packet context
+     * @param egressList The list of egress ports which the multicast packet is intended for.
+     */
+    private void forwardPacketToDst(PacketContext context, ArrayList<ConnectPoint> egressList) {
+
+        // Send the pack out each of the respective egress ports
+        for (ConnectPoint egress : egressList) {
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(egress.port()).build();
+
+            OutboundPacket packet = new DefaultOutboundPacket(
+                    egress.deviceId(),
+                    treatment,
+                    context.inPacket().unparsed());
+
+            packetService.emit(packet);
+        }
+    }
+
+    public static McastRoute createStaticRoute(String source, String group) {
+        checkNotNull(source, "Must provide a source");
+        checkNotNull(group, "Must provide a group");
+        IpAddress ipSource = IpAddress.valueOf(source);
+        IpAddress ipGroup = IpAddress.valueOf(group);
+        return createStaticcreateRoute(ipSource, ipGroup);
+    }
+
+    public static McastRoute createStaticcreateRoute(IpAddress source, IpAddress group) {
+        checkNotNull(source, "Must provide a source");
+        checkNotNull(group, "Must provide a group");
+        McastRoute.Type type = McastRoute.Type.STATIC;
+        return new McastRoute(source, group, type);
+    }
+
+    /**
      * Packet processor responsible for forwarding packets along their paths.
      */
-    private class ReactivePacketProcessor implements PacketProcessor {
 
+    private class ReactivePacketProcessor implements PacketProcessor {
         /**
          * Process incoming packets.
          *
@@ -188,42 +243,75 @@
             // Send the pack out each of the egress devices & port
             forwardPacketToDst(context, egressList);
         }
+
     }
 
-    /**
-     * Forward the packet to it's multicast destinations.
-     *
-     * @param context The packet context
-     * @param egressList The list of egress ports which the multicast packet is intended for.
-     */
-    private void forwardPacketToDst(PacketContext context, ArrayList<ConnectPoint> egressList) {
+    private class McastIntentManager implements McastListener {
 
-        // Send the pack out each of the respective egress ports
-        for (ConnectPoint egress : egressList) {
-            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                    .setOutput(egress.port()).build();
+        private Map<McastRoute, Key> intentHashMap;
 
-            OutboundPacket packet = new DefaultOutboundPacket(
-                    egress.deviceId(),
-                    treatment,
-                    context.inPacket().unparsed());
-
-            packetService.emit(packet);
+        public McastIntentManager() {
+            intentHashMap = new HashMap<>();
         }
-    }
 
-    public static McastRoute createStaticRoute(String source, String group) {
-        checkNotNull(source, "Must provide a source");
-        checkNotNull(group, "Must provide a group");
-        IpAddress ipSource = IpAddress.valueOf(source);
-        IpAddress ipGroup = IpAddress.valueOf(group);
-        return createStaticcreateRoute(ipSource, ipGroup);
-    }
+        @Override
+        public void event(McastEvent event) {
+            McastRoute route = event.subject().route();
+            if (intentHashMap.containsKey(route)) {
+                withdrawIntent(intentHashMap.get(route));
+            }
+            Key routeKey = setIntent(route);
+            intentHashMap.put(route, routeKey);
+        }
 
-    public static McastRoute createStaticcreateRoute(IpAddress source, IpAddress group) {
-        checkNotNull(source, "Must provide a source");
-        checkNotNull(group, "Must provide a group");
-        McastRoute.Type type = McastRoute.Type.STATIC;
-        return new McastRoute(source, group, type);
+        private Key setIntent(McastRoute route) {
+
+            ConnectPoint ingressPoint = mcastRouteManager.fetchSource(route);
+            Set<ConnectPoint> egressPoints = new HashSet<>(mcastRouteManager.fetchSinks(route));
+
+            TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+            TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+
+            if (ingressPoint == null) {
+                log.warn("Can't set intent without an ingress or egress connect points");
+                return null;
+            }
+
+            selector.matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPDst(route.group().toIpPrefix())
+                    .matchIPSrc(route.source().toIpPrefix());
+
+            SinglePointToMultiPointIntent.Builder builder = SinglePointToMultiPointIntent.builder()
+                    .appId(appId)
+                    .selector(selector.build())
+                    .treatment(treatment)
+                    .ingressPoint(ingressPoint);
+
+            // allowing intent to be pushed without egress points means we can drop packets.
+            if (!egressPoints.isEmpty()) {
+                builder.egressPoints(egressPoints);
+            }
+
+            SinglePointToMultiPointIntent intent = builder.build();
+            intentService.submit(intent);
+
+            return intent.key();
+        }
+
+        public void withdrawAllIntents() {
+            for (Map.Entry<McastRoute, Key> entry : intentHashMap.entrySet()) {
+                withdrawIntent(entry.getValue());
+            }
+            intentHashMap.clear();
+        }
+
+        public void withdrawIntent(Key key) {
+            if (key == null) {
+                // nothing to withdraw
+                return;
+            }
+            Intent intent = intentService.getIntent(key);
+            intentService.withdraw(intent);
+        }
     }
 }