- Added MODIFY fucntion in the driver
- Decoupled topology event handler from events notification thread
- Added a logic to handle link failure and backup (using MODIFY-STRICT feature)

Change-Id: I8da952634299068e61b0b969e1d90274f5c4f46e
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java b/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java
index 9451beb..b7fbbc9 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/ECMPShortestPathGraph.java
@@ -10,7 +10,6 @@
 import net.onrc.onos.core.topology.Switch;
 import net.onrc.onos.core.util.Dpid;
 
-import org.projectfloodlight.openflow.util.HexString;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -103,12 +102,14 @@
             }
         }
 
+        /*
         log.debug("ECMPShortestPathGraph:switchSearched for switch {} is {}",
                 HexString.toHexString(rootSwitch.getDpid().value()), switchSearched);
         log.debug("ECMPShortestPathGraph:upstreamLinks for switch {} is {}",
                 HexString.toHexString(rootSwitch.getDpid().value()), upstreamLinks);
         log.debug("ECMPShortestPathGraph:distanceSwitchMap for switch {} is {}",
                 HexString.toHexString(rootSwitch.getDpid().value()), distanceSwitchMap);
+        */
         /*
         for (Integer distance: distanceSwitchMap.keySet()){
                 for (Switch sw: distanceSwitchMap.get(distance)){
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java b/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
index 56a402b..9366a2e 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/IcmpHandler.java
@@ -9,9 +9,7 @@
 package net.onrc.onos.apps.segmentrouting;
 
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Queue;
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IOFSwitch;
@@ -61,8 +59,6 @@
     private IFlowPusherService flowPusher;
     private boolean controllerPortAllowed = false;
 
-    private Queue<IPv4> icmpQueue = new LinkedList<IPv4>();
-
     private static final int TABLE_VLAN = 0;
     private static final int TABLE_TMAC = 1;
     private static final int TABLE_IPv4_UNICAST = 2;
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
index 513b3d6..918a12f 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
@@ -7,9 +7,11 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ScheduledExecutorService;
@@ -34,6 +36,7 @@
 import net.onrc.onos.core.matchaction.MatchAction;
 import net.onrc.onos.core.matchaction.MatchActionId;
 import net.onrc.onos.core.matchaction.MatchActionOperationEntry;
+import net.onrc.onos.core.matchaction.MatchActionOperations.Operator;
 import net.onrc.onos.core.matchaction.action.Action;
 import net.onrc.onos.core.matchaction.action.CopyTtlInAction;
 import net.onrc.onos.core.matchaction.action.CopyTtlOutAction;
@@ -56,6 +59,7 @@
 import net.onrc.onos.core.topology.Port;
 import net.onrc.onos.core.topology.PortData;
 import net.onrc.onos.core.topology.Switch;
+import net.onrc.onos.core.topology.SwitchData;
 import net.onrc.onos.core.topology.TopologyEvents;
 import net.onrc.onos.core.util.Dpid;
 import net.onrc.onos.core.util.IPv4Net;
@@ -65,7 +69,6 @@
 import org.json.JSONException;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
-import org.projectfloodlight.openflow.util.HexString;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -88,6 +91,8 @@
     private IFloodlightProviderService floodlightProvider;
 
     private HashMap<Switch, ECMPShortestPathGraph> graphs;
+    private HashSet<LinkData> topologyLinks;
+    private ConcurrentLinkedQueue<TopologyEvents> topologyEventQueue;
 
     @Override
     public Collection<Class<? extends IFloodlightService>> getModuleServices() {
@@ -129,6 +134,8 @@
         topologyService.addListener(this, false);
         ipPacketQueue = new ConcurrentLinkedQueue<IPv4>();
         graphs = new HashMap<Switch, ECMPShortestPathGraph>();
+        topologyLinks = new HashSet<LinkData>();
+        topologyEventQueue = new ConcurrentLinkedQueue<TopologyEvents>();
 
         this.packetService = context.getServiceImpl(IPacketService.class);
         packetService.registerPacketListener(this);
@@ -142,7 +149,7 @@
         discoveryTask = new SingletonTask(ses, new Runnable() {
             @Override
             public void run() {
-                populateEcmpRoutingRules();
+                handleTopologyChangeEvents();
             }
         });
     }
@@ -244,32 +251,55 @@
      */
     public void topologyEvents(TopologyEvents topologyEvents)
     {
+        topologyEventQueue.add(topologyEvents);
+        discoveryTask.reschedule(500, TimeUnit.MILLISECONDS);
 
-        Collection<LinkData> linkEntriesAdded =
-                topologyEvents.getAddedLinkDataEntries();
-        if (!linkEntriesAdded.isEmpty()) {
-            processLinkAdd(linkEntriesAdded);
-        }
+    }
 
-        Collection<PortData> PortEntriesAdded =
-                topologyEvents.getAddedPortDataEntries();
-        if (linkEntriesAdded != null) {
-            processPortAdd(PortEntriesAdded);
-        }
 
-        Collection<PortData> portEntries =
-                topologyEvents.getRemovedPortDataEntries();
-        if (!portEntries.isEmpty()) {
-            processPortRemoval(portEntries);
-        }
+    private void handleTopologyChangeEvents() {
 
-        Collection<LinkData> linkEntriesRemoved =
-                topologyEvents.getRemovedLinkDataEntries();
-        if (!linkEntriesRemoved.isEmpty()) {
-            processLinkRemoval(linkEntriesRemoved);
+        while (!topologyEventQueue.isEmpty()) {
+            TopologyEvents topologyEvents = topologyEventQueue.poll();
+
+            Collection<LinkData> linkEntriesAdded =
+                    topologyEvents.getAddedLinkDataEntries();
+            if (!linkEntriesAdded.isEmpty()) {
+                processLinkAdd(linkEntriesAdded);
+            }
+
+            Collection<PortData> PortEntriesAdded =
+                    topologyEvents.getAddedPortDataEntries();
+            if (linkEntriesAdded != null) {
+                processPortAdd(PortEntriesAdded);
+            }
+
+            Collection<PortData> portEntries =
+                    topologyEvents.getRemovedPortDataEntries();
+            if (!portEntries.isEmpty()) {
+                processPortRemoval(portEntries);
+            }
+
+            Collection<LinkData> linkEntriesRemoved =
+                    topologyEvents.getRemovedLinkDataEntries();
+            if (!linkEntriesRemoved.isEmpty()) {
+                processLinkRemoval(linkEntriesRemoved);
+            }
+
+            Collection<SwitchData> switchRemoved =
+                    topologyEvents.getRemovedSwitchDataEntries();
+            if (!switchRemoved.isEmpty()) {
+                processSwitchRemoved(switchRemoved);
+            }
+
         }
     }
 
+    private void processSwitchRemoved(Collection<SwitchData> switchRemoved) {
+        /* TODO: We should remove only the links of the switch removed */
+        topologyLinks.clear();
+    }
+
     /**
      * Report ports newly added to driver
      *
@@ -295,10 +325,13 @@
      */
     private void processLinkAdd(Collection<LinkData> linkEntries) {
 
+        boolean linkRecovered = false;
+
         // TODO: How to determine this link was broken before and back now
         // If so, we need to ad the link slowly...
         // Or, just add any new or restored link slowly ???
         for (LinkData link : linkEntries) {
+
             SwitchPort srcPort = link.getSrc();
             SwitchPort dstPort = link.getDst();
 
@@ -309,8 +342,21 @@
 
             srcSw.addPortToGroups(srcPort.getPortNumber());
             dstSw.addPortToGroups(dstPort.getPortNumber());
+
+
+            if (!topologyLinks.contains(link)) {
+                topologyLinks.add(link);
+            }
+            else {
+                linkRecovered = true;
+            }
         }
-        discoveryTask.reschedule(1, TimeUnit.SECONDS);
+        if (linkRecovered) {
+            populateEcmpRoutingRules(true);
+        }
+        else {
+            populateEcmpRoutingRules(false);
+        }
     }
 
     /**
@@ -338,10 +384,10 @@
 
             Switch srcSwitch = mutableTopology.getSwitch(srcPort.getDpid());
             if (srcSwitch.getLinkToNeighbor(dstPort.getDpid()) == null) {
-                //discoveryTask.reschedule(1, TimeUnit.SECONDS);
-                modifyEcmpRoutingRules(link);
+                //modifyEcmpRoutingRules(link);
+                populateEcmpRoutingRules(true);
                 log.debug("All links are gone b/w {} and {}", srcPort.getDpid(),
-                        srcPort.getDpid());
+                        dstPort.getDpid());
             }
         }
     }
@@ -367,20 +413,23 @@
      * Populate routing rules walking through the ECMP shortest paths
      *
      */
-    private void populateEcmpRoutingRules() {
-
+    private void populateEcmpRoutingRules(boolean modified) {
+        graphs.clear();
         Iterable<Switch> switches = mutableTopology.getSwitches();
         for (Switch sw : switches) {
             ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw);
             graphs.put(sw, ecmpSPG);
             //log.debug("ECMPShortestPathGraph is computed for switch {}",
             //        HexString.toHexString(sw.getDpid().value()));
-            populateEcmpRoutingRulesForPath(sw, ecmpSPG);
+            populateEcmpRoutingRulesForPath(sw, ecmpSPG, modified);
+            if (modified) {
+                log.debug("Modify the rules for {}" , sw.getDpid());
+            }
         }
     }
 
     private void populateEcmpRoutingRulesForPath(Switch sw,
-            ECMPShortestPathGraph ecmpSPG) {
+            ECMPShortestPathGraph ecmpSPG, boolean modified) {
 
         HashMap<Integer, HashMap<Switch, ArrayList<ArrayList<Dpid>>>> switchVia =
                 ecmpSPG.getAllLearnedSwitchesAndVia();
@@ -392,14 +441,13 @@
             HashMap<Switch, ArrayList<ArrayList<Dpid>>> swViaMap =
                     switchVia.get(itrIdx);
             for (Switch targetSw : swViaMap.keySet()) {
-                log.debug("ECMPShortestPathGraph:****switch {} via:",
-                        HexString.toHexString(targetSw.getDpid().value()));
+                //log.debug("ECMPShortestPathGraph:****switch {} via:",
+                //        HexString.toHexString(targetSw.getDpid().value()));
                 String destSw = sw.getDpid().toString();
                 List<String> fwdToSw = new ArrayList<String>();
 
-                int i = 0;
                 for (ArrayList<Dpid> via : swViaMap.get(targetSw)) {
-                    log.debug("ECMPShortestPathGraph:******{}) {}", ++i, via);
+                    //log.debug("ECMPShortestPathGraph:******{}) {}", ++i, via);
                     if (via.isEmpty()) {
                         fwdToSw.add(destSw);
                     }
@@ -407,21 +455,23 @@
                         fwdToSw.add(via.get(0).toString());
                     }
                 }
-                setRoutingRule(targetSw, destSw, fwdToSw);
+                setRoutingRule(targetSw, destSw, fwdToSw, modified);
             }
 
             // Send Barrier Message and make sure all rules are set
             // before we set the rules to next routers
             IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
                     getSwId(sw.getDpid().toString()));
-            try {
-                OFBarrierReplyFuture replyFuture = sw13.sendBarrier();
-                replyFuture.get(10, TimeUnit.SECONDS);
-            } catch (IOException e) {
-                e.printStackTrace();
-            } catch (InterruptedException | ExecutionException | TimeoutException e) {
-                log.error("Barrier message not received for sw: {}", sw.getDpid());
-                e.printStackTrace();
+            if (sw13 != null) {
+                try {
+                    OFBarrierReplyFuture replyFuture = sw13.sendBarrier();
+                    replyFuture.get(10, TimeUnit.SECONDS);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } catch (InterruptedException | ExecutionException | TimeoutException e) {
+                    log.error("Barrier message not received for sw: {}", sw.getDpid());
+                    e.printStackTrace();
+                }
             }
         }
 
@@ -456,7 +506,7 @@
     private void modifyEcmpRoutingRules(LinkData linkRemoved) {
 
         //HashMap<Switch, SwitchPair> linksToRecompute = new HashMap<Switch, SwitchPair>();
-        List<SwitchPair> linksToRecompute = new ArrayList<SwitchPair>();
+        Set<SwitchPair> linksToRecompute = new HashSet<SwitchPair>();
 
         for (ECMPShortestPathGraph ecmpSPG : graphs.values()) {
             Switch rootSw = ecmpSPG.getRootSwitch();
@@ -466,7 +516,16 @@
                 for (Switch destSw: p.keySet()) {
                     ArrayList<Path> path = p.get(destSw);
                     if  (checkPath(path, linkRemoved)) {
-                        linksToRecompute.add(new SwitchPair(rootSw, destSw));
+                        boolean found = false;
+                        for (SwitchPair pair: linksToRecompute) {
+                            if (pair.getSource().getDpid() == rootSw.getDpid() &&
+                                    pair.getSource().getDpid() == destSw.getDpid()) {
+                                found = true;
+                            }
+                        }
+                        if (!found) {
+                            linksToRecompute.add(new SwitchPair(rootSw, destSw));
+                        }
                     }
                 }
             }
@@ -482,7 +541,7 @@
             ECMPShortestPathGraph ecmpSPG =
                     new ECMPShortestPathGraph(pair.getSource());
             // TODO: we need to use different function to MODIFY the rules
-            populateEcmpRoutingRulesForPath(pair.getSource(), ecmpSPG);
+            populateEcmpRoutingRulesForPath(pair.getSource(), ecmpSPG, true);
         }
     }
 
@@ -498,8 +557,11 @@
         for (Path ppp: path) {
             // TODO: need to check if this is a bidirectional or
             // unidirectional
-            if (ppp.contains(linkRemoved)) {
-                return true;
+            for (LinkData link: ppp) {
+                //if (link.equals(linkRemoved))
+                if (link.getDst().getDpid().equals(linkRemoved.getDst().getDpid()) &&
+                        link.getSrc().getDpid().equals(linkRemoved.getSrc().getDpid()))
+                    return true;
             }
         }
 
@@ -520,7 +582,8 @@
      * @param destSw Final destination switches
      * @param fwdToSw next hop switches
      */
-    private void setRoutingRule(Switch targetSw, String destSw, List<String> fwdToSw) {
+    private void setRoutingRule(Switch targetSw, String destSw, List<String> fwdToSw,
+            boolean modified) {
 
         if (fwdToSw.isEmpty()) {
             fwdToSw.add(destSw);
@@ -534,10 +597,11 @@
             Switch destSwitch = mutableTopology.getSwitch(new Dpid(destSw));
             String subnets = destSwitch.getStringAttribute("subnets");
             setIpTableRouterSubnet(targetSw, subnets, getMplsLabel(destSw)
-                    , fwdToSw);
+                    , fwdToSw, modified);
 
             String routerIp = destSwitch.getStringAttribute("routerIp");
-            setIpTableRouter(targetSw, routerIp, getMplsLabel(destSw), fwdToSw, null);
+            setIpTableRouter(targetSw, routerIp, getMplsLabel(destSw), fwdToSw,
+                    null, modified);
         }
         // Only if the target switch is the edge router, then set the IP rules
         else if (IsEdgeRouter(targetSw.getDpid().toString())) {
@@ -545,11 +609,12 @@
             // routers
             Switch destSwitch = mutableTopology.getSwitch(new Dpid(destSw));
             String routerIp = destSwitch.getStringAttribute("routerIp");
-            setIpTableRouter(targetSw, routerIp, getMplsLabel(destSw), fwdToSw, null);
+            setIpTableRouter(targetSw, routerIp, getMplsLabel(destSw), fwdToSw,
+                    null, modified);
         }
         // if it is a transit router, then set rules in the MPLS table
         else {
-            setMplsTable(targetSw, getMplsLabel(destSw), fwdToSw);
+            setMplsTable(targetSw, getMplsLabel(destSw), fwdToSw, modified);
         }
 
     }
@@ -563,7 +628,7 @@
      * @param fwdToSw  router to forward packets to
      */
     private void setIpTableRouterSubnet(Switch targetSw, String subnets,
-            String mplsLabel, List<String> fwdToSw) {
+            String mplsLabel, List<String> fwdToSw, boolean modified) {
 
         Collection<MatchActionOperationEntry> entries =
                 new ArrayList<MatchActionOperationEntry>();
@@ -572,7 +637,8 @@
             JSONArray arry = new JSONArray(subnets);
             for (int i = 0; i < arry.length(); i++) {
                 String subnetIp = (String) arry.getJSONObject(i).get("subnetIp");
-                setIpTableRouter(targetSw, subnetIp, mplsLabel, fwdToSw, entries);
+                setIpTableRouter(targetSw, subnetIp, mplsLabel, fwdToSw, entries,
+                        modified);
             }
         } catch (JSONException e) {
             e.printStackTrace();
@@ -627,7 +693,8 @@
      * @param entries
      */
     private void setIpTableRouter(Switch sw, String subnetIp, String mplsLabel,
-            List<String> fwdToSws, Collection<MatchActionOperationEntry> entries) {
+            List<String> fwdToSws, Collection<MatchActionOperationEntry> entries,
+            boolean modified) {
 
         Ipv4Match ipMatch = new Ipv4Match(subnetIp);
         List<Action> actions = new ArrayList<>();
@@ -677,22 +744,29 @@
         MatchAction matchAction = new MatchAction(new MatchActionId(0),
                 new SwitchPort((long) 0, (short) 0), ipMatch, actions);
 
+        Operator operator = null;
+        if (modified)
+            operator =  Operator.MODIFY;
+        else
+            operator = Operator.ADD;
+
         MatchActionOperationEntry maEntry =
-                new MatchActionOperationEntry(
-                        net.onrc.onos.core.matchaction.MatchActionOperations.Operator.ADD,
-                        matchAction);
+                new MatchActionOperationEntry(operator, matchAction);
 
         IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
                 getSwId(sw.getDpid().toString()));
 
-        try {
-            printMatchActionOperationEntry(sw, maEntry);
-            if (entries != null)
-                entries.add(maEntry);
-            else
-                sw13.pushFlow(maEntry);
-        } catch (IOException e) {
-            e.printStackTrace();
+        /* TODO: we should check the SWICH REMOVED events */
+        if (sw13 != null) {
+            try {
+                printMatchActionOperationEntry(sw, maEntry);
+                if (entries != null)
+                    entries.add(maEntry);
+                else
+                    sw13.pushFlow(maEntry);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
         }
 
     }
@@ -724,7 +798,8 @@
      * @param mplsLabel destination MPLS label
      * @param fwdSws next hop switches
      */
-    private void setMplsTable(Switch sw, String mplsLabel, List<String> fwdSws) {
+    private void setMplsTable(Switch sw, String mplsLabel, List<String> fwdSws,
+            boolean modified) {
 
         MplsMatch mplsMatch = new MplsMatch(Integer.parseInt(mplsLabel));
 
@@ -759,19 +834,25 @@
         MatchAction matchAction = new MatchAction(new MatchActionId(0),
                 new SwitchPort((long) 0, (short) 0), mplsMatch, actions);
 
+        Operator operator = null;
+        if (modified)
+            operator =  Operator.MODIFY;
+        else
+            operator = Operator.ADD;
+
         MatchActionOperationEntry maEntry =
-                new MatchActionOperationEntry(
-                        net.onrc.onos.core.matchaction.MatchActionOperations.Operator.ADD,
-                        matchAction);
+                new MatchActionOperationEntry(operator, matchAction);
 
         IOF13Switch sw13 = (IOF13Switch) floodlightProvider.getMasterSwitch(
                 getSwId(sw.getDpid().toString()));
 
-        try {
-            printMatchActionOperationEntry(sw, maEntry);
-            sw13.pushFlow(maEntry);
-        } catch (IOException e) {
-            e.printStackTrace();
+        if (sw13 != null) {
+            try {
+                printMatchActionOperationEntry(sw, maEntry);
+                sw13.pushFlow(maEntry);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
         }
 
     }
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
index 0e21e63..f21e293 100644
--- a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
@@ -210,6 +210,8 @@
 
     public void removePortFromGroups(PortNumber port) {
         ArrayList<NeighborSet> portNSSet = portNeighborSetMap.get(port);
+        if (portNSSet == null)
+            return;
         for (NeighborSet ns : portNSSet) {
             /* Delete the first matched bucket */
             Iterator<BucketInfo> it = ecmpGroups.get(ns).buckets.iterator();
@@ -937,9 +939,9 @@
         case REMOVE:
             fmBuilder = factory.buildFlowDeleteStrict();
             break;
-        // case MODIFY: // TODO
-        // fmBuilder = factory.buildFlowModifyStrict();
-        // break;
+        case MODIFY: // TODO
+            fmBuilder = factory.buildFlowModifyStrict();
+            break;
         default:
             log.warn("Unsupported MatchAction Operator: {}", op);
             return null;
@@ -1002,9 +1004,9 @@
         case REMOVE:
             fmBuilder = factory.buildFlowDeleteStrict();
             break;
-        // case MODIFY: // TODO
-        // fmBuilder = factory.buildFlowModifyStrict();
-        // break;
+         case MODIFY: // TODO
+            fmBuilder = factory.buildFlowModifyStrict();
+            break;
         default:
             log.warn("Unsupported MatchAction Operator: {}", op);
             return null;
@@ -1105,9 +1107,9 @@
         case REMOVE:
             fmBuilder = factory.buildFlowDeleteStrict();
             break;
-        // case MODIFY: // TODO
-        // fmBuilder = factory.buildFlowModifyStrict();
-        // break;
+        case MODIFY: // TODO
+            fmBuilder = factory.buildFlowModifyStrict();
+            break;
         default:
             log.warn("Unsupported MatchAction Operator: {}", op);
             return null;
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/FlowPusher.java b/src/main/java/net/onrc/onos/core/flowprogrammer/FlowPusher.java
index 27f1a3c..4ea528a 100644
--- a/src/main/java/net/onrc/onos/core/flowprogrammer/FlowPusher.java
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/FlowPusher.java
@@ -714,9 +714,9 @@
         case REMOVE:
             fmBuilder = factory.buildFlowDeleteStrict();
             break;
-        // case MODIFY: // TODO
-        // fmBuilder = factory.buildFlowModifyStrict();
-        // break;
+        case MODIFY:
+            fmBuilder = factory.buildFlowModifyStrict();
+            break;
         default:
             log.warn("Unsupported MatchAction Operator: {}", matchActionOp.getOperator());
             return;
diff --git a/src/main/java/net/onrc/onos/core/matchaction/MatchActionComponent.java b/src/main/java/net/onrc/onos/core/matchaction/MatchActionComponent.java
index 3ee164f..387ef28 100644
--- a/src/main/java/net/onrc/onos/core/matchaction/MatchActionComponent.java
+++ b/src/main/java/net/onrc/onos/core/matchaction/MatchActionComponent.java
@@ -215,6 +215,7 @@
                 SwitchPort sw = matchAction.getSwitchPort();
                 switches.put(sw.getDpid(), new SwitchResult(setId, sw.getDpid()));
                 switch(matchActionOp.getOperator()) {
+                case MODIFY:
                 case ADD:
                     matchActionMap.put(matchAction.getId(), matchAction);
                     break;
diff --git a/src/main/java/net/onrc/onos/core/matchaction/MatchActionOperations.java b/src/main/java/net/onrc/onos/core/matchaction/MatchActionOperations.java
index 7d9063f..e912956 100644
--- a/src/main/java/net/onrc/onos/core/matchaction/MatchActionOperations.java
+++ b/src/main/java/net/onrc/onos/core/matchaction/MatchActionOperations.java
@@ -27,6 +27,11 @@
 
         /** Remove an existing match action. */
         REMOVE,
+
+        /*** Modify an existing match action entry strictly matching wildcards
+         * and priority (works as MODIFY StRICT). */
+        MODIFY,
+
     }
 
     /**