Updated mfwd delete behavior and fixed some small bugs

Change-Id: Id354a88507e94b83fd0007c084cdbf35093bbd1a
diff --git a/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java b/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java
index ae5d9e9..c794c80 100644
--- a/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java
+++ b/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java
@@ -37,9 +37,32 @@
             required = true, multiValued = false)
     String gAddr = null;
 
+    @Argument(index = 2, name = "egressList",
+            description = "Egress id/port",
+            required = false, multiValued = true)
+    String[] egressList = null;
+
+
     @Override
     protected void execute() {
+
+        boolean deleted = false;
         McastRouteTable mrib = McastRouteTable.getInstance();
-        mrib.removeRoute(sAddr, gAddr);
+
+        if (egressList == null) {
+            mrib.removeRoute(sAddr, gAddr);
+            deleted = true;
+        } else {
+            // check list for validity before we begin to delete.
+            for (String egress : egressList) {
+                deleted = mrib.removeEgress(sAddr, gAddr, egress);
+            }
+        }
+
+        if (deleted) {
+            print("Successful delete");
+        } else {
+            print("Failed to delete");
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java
index 90f65c9..b7f1f3c 100644
--- a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java
+++ b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java
@@ -79,8 +79,7 @@
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
         TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
 
-        if (mroute.getIngressPoint() == null ||
-                mroute.getEgressPoints().isEmpty()) {
+        if (mroute.getIngressPoint() == null) {
             return null;
         }
 
@@ -96,16 +95,22 @@
                 .matchIPDst(mroute.getGaddr())
                 .matchIPSrc(mroute.getSaddr());
 
-        SinglePointToMultiPointIntent intent =
-                SinglePointToMultiPointIntent.builder()
+
+        SinglePointToMultiPointIntent.Builder builder =  SinglePointToMultiPointIntent.builder()
                         .appId(McastForwarding.getAppId())
                         .selector(selector.build())
                         .treatment(treatment)
-                        .ingressPoint(mroute.getIngressPoint().getConnectPoint())
-                        .egressPoints(mroute.getEgressConnectPoints()).
-                        build();
+                        .ingressPoint(mroute.getIngressPoint().getConnectPoint());
 
+        // allowing intent to be pushed without egress points means we can drop packets.
+        if (!mroute.getEgressPoints().isEmpty()) {
+            builder.egressPoints(mroute.getEgressConnectPoints());
+        }
+
+        SinglePointToMultiPointIntent intent = builder.build();
         intentService.submit(intent);
+        mroute.setDirty(false);
+
         return intent;
     }
 
@@ -114,9 +119,10 @@
      *
      * @param mroute the mcast route whose intent we want to remove
      */
-    public void withdrawIntent(McastRouteBase mroute) {
+    public void withdrawIntent(McastRoute mroute) {
         Intent intent = intentService.getIntent(mroute.getIntentKey());
         intentService.withdraw(intent);
+        mroute.setDirty(false);
     }
 
     /**
diff --git a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java
index 12b7e6d..a67725d 100644
--- a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java
+++ b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java
@@ -57,6 +57,21 @@
     public boolean isIp6();
 
     /**
+     * Get the dirty state.
+     *
+     * @return whether this route is dirty or not.
+     */
+    public boolean getDirty();
+
+    /**
+     * Set the dirty state to indicate that something changed.
+     * This may require an update to the flow tables (intents).
+     *
+     * @param dirty set the dirty bit
+     */
+    public void setDirty(boolean dirty);
+
+    /**
      * Add the ingress ConnectPoint.
      *
      * @param cpstr string representing a ConnectPoint
diff --git a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java
index 730acfa..4da1f33 100644
--- a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java
+++ b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java
@@ -17,7 +17,6 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-import org.apache.commons.collections.set.ListOrderedSet;
 import org.onlab.packet.IpPrefix;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.intent.SinglePointToMultiPointIntent;
@@ -267,6 +266,33 @@
     }
 
     /**
+     * Remove an egress from McastConnectPoint.
+     *
+     * @param connectPoint the egress connect point
+     * @return boolean result of removal
+     */
+    public boolean removeEgressPoint(String connectPoint) {
+        checkNotNull(connectPoint);
+        return this.removeEgressPoint(ConnectPoint.deviceConnectPoint(connectPoint));
+    }
+
+    /**
+     * Remove an egress from McastConnectPoint.
+     *
+     * @param cp the egress connect point
+     * @return boolean result of removal
+     */
+    public boolean removeEgressPoint(ConnectPoint cp) {
+        boolean removed = false;
+        McastConnectPoint mcp = this.findEgressConnectPoint(checkNotNull(cp));
+        if (mcp != null) {
+            removed = egressPoints.remove(mcp);
+            setDirty(true);
+        }
+        return removed;
+    }
+
+    /**
      * Add an egress McastConnectPoint.
      *
      * @param cpstr deviceId/port of the connect point
@@ -292,7 +318,7 @@
      * @return Set of egress ConnectPoints
      */
     public Set<ConnectPoint> getEgressConnectPoints() {
-        Set<ConnectPoint> cps = new ListOrderedSet();
+        Set<ConnectPoint> cps = new HashSet<ConnectPoint>();
 
         for (McastConnectPoint mcp : egressPoints) {
             cps.add(mcp.getConnectPoint());
@@ -417,7 +443,7 @@
         out += "intent: ";
         out += (intentKey == null) ? "not installed" : this.intentKey.toString();
         out += "\n\tingress: ";
-        out += (ingressPoint == null) ? "NULL" : ingressPoint.toString();
+        out += (ingressPoint == null) ? "NULL" : ingressPoint.getConnectPoint().toString();
         out += "\n\tegress: {\n";
         if (egressPoints != null && !egressPoints.isEmpty()) {
             for (McastConnectPoint eg : egressPoints) {
diff --git a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java
index 5a07bec..1140c3a 100644
--- a/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java
+++ b/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java
@@ -17,6 +17,7 @@
 
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.IpPrefix;
+
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -191,6 +192,30 @@
     }
 
     /**
+     * Delete a specific egress from the MRIB.
+     *
+     * @param saddr source address * or x.x.x.x or x.x.x.x/y
+     * @param gaddr group address x.x.x.x or x.x.x.x/y
+     * @param egress group address x.x.x.x or x.x.x.x/y
+     * @return boolean if egress was deleted
+     */
+    public boolean removeEgress(String saddr, String gaddr, String egress) {
+
+        IpPrefix gpfx = IpPrefix.valueOf(gaddr);
+        IpPrefix spfx = IpPrefix.valueOf(0, 0);
+        if (saddr != null && !saddr.equals("*")) {
+            spfx = IpPrefix.valueOf(saddr);
+        }
+
+        McastRouteSource src = (McastRouteSource) findBestMatch(spfx, gpfx);
+        boolean removed = src.removeEgressPoint(egress);
+        if (removed) {
+            src.setIntent();
+        }
+        return removed;
+    }
+
+    /**
      * Delete a multicast route from the MRIB.
      *
      * @param saddr source address * or x.x.x.x or x.x.x.x/y