using MultiPointToSinglePointIntent

1. adapt SDN-IP existing code to the new intent framework
2. add comments to methods
3. change to PacketMatchBuilder and IdBlockAllocatorBasedIntentIdGenerator
4. clean up the code in SdnIp.java, which includes:
    TopologyChangeDetector, checkTopologyReady,
    setupFullMesh for layer2 forwarding, flow path relative code,
    device storage relative code, flow cache relative code,
    flowMod and flowId relative code, and IOFSwitchListener.

This change is for ONOS-1858 (not including the code testing).

Change-Id: I19c1dcca3a2e2a67b307edf566279b7e8554bb84
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/ISdnIpService.java b/src/main/java/net/onrc/onos/apps/sdnip/ISdnIpService.java
index 6189aa7..a2abaa1 100644
--- a/src/main/java/net/onrc/onos/apps/sdnip/ISdnIpService.java
+++ b/src/main/java/net/onrc/onos/apps/sdnip/ISdnIpService.java
@@ -52,5 +52,6 @@
     /**
      * Start SDN-IP Routing.
      */
-    public void beginRoutingNew();
+    public void beginRouting();
+
 }
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/Prefix.java b/src/main/java/net/onrc/onos/apps/sdnip/Prefix.java
index d4e730d..81010d6 100644
--- a/src/main/java/net/onrc/onos/apps/sdnip/Prefix.java
+++ b/src/main/java/net/onrc/onos/apps/sdnip/Prefix.java
@@ -192,6 +192,16 @@
         return Arrays.copyOf(address, address.length);
     }
 
+    /**
+     * Gets the InetAddress.
+     *
+     * @return the inetAddress
+     */
+    public InetAddress getInetAddress() {
+        return inetAddress;
+    }
+
+
     @Override
     public boolean equals(Object other) {
         if (!(other instanceof Prefix)) {
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java b/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
index d6713d5..35c3eaa 100644
--- a/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
+++ b/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
@@ -6,25 +6,25 @@
 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.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.LinkedBlockingQueue;
 
-import net.floodlightcontroller.core.IFloodlightProviderService;
-import net.floodlightcontroller.core.IOFSwitch;
-import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
-import net.floodlightcontroller.core.IOFSwitchListener;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.api.newintent.IntentService;
+import net.onrc.onos.api.newintent.MultiPointToSinglePointIntent;
 import net.onrc.onos.apps.proxyarp.IArpRequester;
 import net.onrc.onos.apps.proxyarp.IProxyArpService;
 import net.onrc.onos.apps.sdnip.RibUpdate.Operation;
@@ -36,8 +36,13 @@
 import net.onrc.onos.core.intent.runtime.IPathCalcRuntimeService;
 import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
 import net.onrc.onos.core.main.config.IConfigInfoService;
+import net.onrc.onos.core.matchaction.action.ModifyDstMacAction;
+import net.onrc.onos.core.matchaction.match.PacketMatch;
+import net.onrc.onos.core.matchaction.match.PacketMatchBuilder;
+import net.onrc.onos.core.newintent.IdBlockAllocatorBasedIntentIdGenerator;
 import net.onrc.onos.core.registry.IControllerRegistryService;
 import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.IPv4;
 import net.onrc.onos.core.util.PortNumber;
 import net.onrc.onos.core.util.SwitchPort;
 import net.sf.json.JSONArray;
@@ -49,7 +54,6 @@
 import org.codehaus.jackson.JsonParseException;
 import org.codehaus.jackson.map.JsonMappingException;
 import org.codehaus.jackson.map.ObjectMapper;
-import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -63,12 +67,15 @@
 import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
 import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
 
+/**
+ * This class sets up BGP paths, handles RIB updates and relative intents.
+ * TODO: Thread-safe.
+ */
 public class SdnIp implements IFloodlightModule, ISdnIpService,
-        IArpRequester, IOFSwitchListener, IConfigInfoService {
+        IArpRequester, IConfigInfoService {
 
     private static final Logger log = LoggerFactory.getLogger(SdnIp.class);
 
-    private IFloodlightProviderService floodlightProvider;
     private ILinkDiscoveryService linkDiscoveryService;
     private IRestApiService restApi;
     private IProxyArpService proxyArp;
@@ -86,31 +93,13 @@
     /* ShortestPath Intent Variables */
     private final String caller = "SdnIp";
     private IControllerRegistryService controllerRegistryService;
+    private IntentService intentService;
     private IPathCalcRuntimeService pathRuntime;
     /* Shortest Intent Path Variables */
+    private IdBlockAllocatorBasedIntentIdGenerator intentIdGenerator;
+    //private static final short ARP_PRIORITY = 20;
 
-    private static final short ARP_PRIORITY = 20;
-
-    // The fields below are unused after the move to FlowManager.
-    // Remove them if no longer needed.
-    /*
-    // We need to identify our flows somehow, in lieu of an OS-wide mechanism
-    // to hand out cookie IDs to prevent conflicts.
-    private static final long APP_COOKIE = 0xa0000000000000L;
-    // Cookie for flows that do L2 forwarding within SDN domain to egress routers
-    private static final long L2_FWD_COOKIE = APP_COOKIE + 1;
-    // Cookie for flows in ingress switches that rewrite the MAC address
-    private static final long MAC_RW_COOKIE = APP_COOKIE + 2;
-    // Cookie for flows that setup BGP paths
-    private static final long BGP_COOKIE = APP_COOKIE + 3;
-    // Forwarding uses priority 0, and the mac rewrite entries in ingress switches
-    // need to be higher priority than this otherwise the rewrite may not get done
-    private static final short SDNIP_PRIORITY = 10;
-    */
-
-    private static final short BGP_PORT = 179;
-
-    private static final int TOPO_DETECTION_WAIT = 2; // seconds
+    //private static final short BGP_PORT = 179;
 
     // Configuration stuff
     private List<String> switches;
@@ -120,70 +109,18 @@
     private MACAddress bgpdMacAddress;
     private short vlan;
 
-    // True when all switches have connected
-    private volatile boolean switchesConnected = false;
-    // True when we have a full mesh of shortest paths between gateways
-    private volatile boolean topologyReady = false;
-
-    // private SingletonTask topologyChangeDetectorTask;
-
     private SetMultimap<InetAddress, RibUpdate> prefixesWaitingOnArp;
 
-    private Map<InetAddress, Path> pathsWaitingOnArp;
-
     private ExecutorService bgpUpdatesExecutor;
 
-    private Map<InetAddress, Path> pushedPaths;
-    private Map<Prefix, Path> prefixToPath;
-    // private Multimap<Prefix, PushedFlowMod> pushedFlows;
-    //private Multimap<Prefix, FlowId> pushedFlowIds;
+    private ConcurrentHashMap<Prefix, MultiPointToSinglePointIntent> pushedRouteIntents;
 
-    // XXX FlowCache has been removed
-    //private FlowCache flowCache;
-
-    // TODO: Fix for the new Topology Network Graph
-    // private volatile Topology topology = null;
-
-    /*
-    private class TopologyChangeDetector implements Runnable {
-        @Override
-        public void run() {
-            log.debug("Running topology change detection task");
-            // TODO: Fix the code below after topoLinkService was removed
-            synchronized (linkUpdates) {
-
-                ITopoLinkService topoLinkService = new TopoLinkServiceImpl();
-
-                List<Link> activeLinks = topoLinkService.getActiveLinks();
-
-                Iterator<LDUpdate> it = linkUpdates.iterator();
-                while (it.hasNext()){
-                    LDUpdate ldu = it.next();
-                    Link l = new Link(ldu.getSrc(), ldu.getSrcPort(),
-                            ldu.getDst(), ldu.getDstPort());
-
-                    if (activeLinks.contains(l)){
-                        it.remove();
-                    }
-                }
-            }
-
-            if (!topologyReady) {
-                if (linkUpdates.isEmpty()) {
-                    // All updates have been seen in network map.
-                    // We can check if topology is ready
-                    log.debug("No known changes outstanding. Checking topology now");
-                    checkStatus();
-                } else {
-                    // We know of some link updates that haven't propagated to the database yet
-                    log.debug("Some changes not found in network map - {} links missing", linkUpdates.size());
-                    topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
-                }
-            }
-        }
-    }
-    */
-
+    /**
+     * SDN-IP application has a configuration file. This method is to read all
+     * the info from this file.
+     *
+     * @param configFilename the name of configuration file for SDN-IP application
+     */
     private void readConfiguration(String configFilename) {
         File gatewaysFile = new File(configFilename);
         ObjectMapper mapper = new ObjectMapper();
@@ -215,7 +152,7 @@
             throw new ConfigurationRuntimeException("Error in JSON file", e);
         }
 
-        // Populate the interface Patricia Tree
+        // Populate the interface InvertedRadixTree
         for (Interface intf : interfaces.values()) {
             Prefix prefix = new Prefix(intf.getIpAddress().getAddress(),
                     intf.getPrefixLength());
@@ -242,7 +179,6 @@
     @Override
     public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
         Collection<Class<? extends IFloodlightService>> l = new ArrayList<>();
-        l.add(IFloodlightProviderService.class);
         l.add(IRestApiService.class);
         l.add(IControllerRegistryService.class);
         l.add(IPathCalcRuntimeService.class);
@@ -260,30 +196,23 @@
 
         ribUpdates = new LinkedBlockingQueue<>();
 
-        // Register floodlight provider and REST handler.
-        floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
-        linkDiscoveryService = context.getServiceImpl(ILinkDiscoveryService.class);
+        // Register REST handler.
         restApi = context.getServiceImpl(IRestApiService.class);
         proxyArp = context.getServiceImpl(IProxyArpService.class);
 
         controllerRegistryService = context
                 .getServiceImpl(IControllerRegistryService.class);
-        pathRuntime = context.getServiceImpl(IPathCalcRuntimeService.class);
 
-        // ScheduledExecutorService executor =
-        // Executors.newScheduledThreadPool(1);
-        // topologyChangeDetectorTask = new SingletonTask(executor, new
-        // TopologyChangeDetector());
+        intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(
+                controllerRegistryService);
 
-        pathsWaitingOnArp = new HashMap<>();
+        // TODO: initialize intentService
+
+        pushedRouteIntents = new ConcurrentHashMap<>();
+
         prefixesWaitingOnArp = Multimaps.synchronizedSetMultimap(
                 HashMultimap.<InetAddress, RibUpdate>create());
 
-        pushedPaths = new HashMap<>();
-        prefixToPath = new HashMap<>();
-        // pushedFlows = HashMultimap.<Prefix, PushedFlowMod>create();
-        // pushedFlowIds = HashMultimap.create();
-
         //flowCache = new FlowCache(floodlightProvider);
 
         bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
@@ -321,7 +250,6 @@
     public void startUp(FloodlightModuleContext context) {
         restApi.addRestletRoutable(new SdnIpWebRoutable());
         restApi.addRestletRoutable(new SdnIpWebRoutableNew());
-        floodlightProvider.addOFSwitchListener(this);
 
         // Retrieve the RIB from BGPd during startup
         retrieveRib();
@@ -347,6 +275,12 @@
         return routerId;
     }
 
+    /**
+     * SDN-IP application will fetch all rib entries from BGPd when it starts.
+     * Especially when we restart SDN-IP application while the BGPd has been
+     * running all the time. Then before SDN-IP application re-connects to BGPd,
+     * there are already lots of rib entries in BGPd.
+     */
     private void retrieveRib() {
         String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
         String response = RestClient.get(url);
@@ -400,6 +334,11 @@
         }
     }
 
+    /**
+     * Put RIB update to RIB update queue.
+     *
+     * @param update RIB update
+     */
     @Override
     public void newRibUpdate(RibUpdate update) {
         try {
@@ -410,6 +349,15 @@
         }
     }
 
+    /**
+     * Process adding RIB update.
+     * Put new RIB update into InvertedRadixTree. If there was an existing nexthop
+     * for this prefix, but the next hop was different, then execute deleting old
+     * RIB update. If the next hop is the SDN domain, we do not handle it at the
+     * moment. Otherwise, execute adding RIB.
+     *
+     * @param update RIB update
+     */
     private void processRibAdd(RibUpdate update) {
         synchronized (this) {
             Prefix prefix = update.getPrefix();
@@ -420,10 +368,9 @@
 
             if (rib != null && !rib.equals(update.getRibEntry())) {
                 // There was an existing nexthop for this prefix. This update
-                // supersedes that,
-                // so we need to remove the old flows for this prefix from the
-                // switches
-                executeDeletePrefix(prefix, rib);
+                // supersedes that, so we need to remove the old flows for this
+                // prefix from the switches
+                executeDeleteRoute(prefix, rib);
             }
 
             if (update.getRibEntry().getNextHop().equals(
@@ -439,405 +386,157 @@
         }
     }
 
+    /**
+     * Execute adding RIB update.
+     * Find out the egress Interface and MAC address of next hop router for this
+     * RIB update. If the MAC address can not be found in ARP cache, then this
+     * prefix will be put in prefixesWaitingOnArp queue. Otherwise, new flow
+     * intent will be created and installed.
+     *
+     * @param update RIB update
+     */
     private void executeRibAdd(RibUpdate update) {
-        // TODO: Fix the code below. Note that "deviceStorage" was removed.
 
-        /*
         Prefix prefix = update.getPrefix();
         RibEntry rib = update.getRibEntry();
 
-        InetAddress dstIpAddress = rib.getNextHop();
-        MACAddress nextHopMacAddress = null;
-
-        // See if we know the MAC address of the next hop
-        // TODO if we do not treat the next hop as a device in the future, we need to update this
-        IDeviceObject nextHopDevice =
-                deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(dstIpAddress));
-
-        if (nextHopDevice == null){
-            log.debug("NextHopDevice for IP: {} is null", dstIpAddress);
-            prefixesWaitingOnArp.put(dstIpAddress,
-                    new RibUpdate(Operation.UPDATE, prefix, rib));
-            proxyArp.sendArpRequest(dstIpAddress, this, true);
-            return;
-
-        }
-        nextHopMacAddress = MACAddress.valueOf(nextHopDevice.getMACAddress());
+        InetAddress nextHopIpAddress = rib.getNextHop();
 
         // Find the attachment point (egress interface) of the next hop
         Interface egressInterface = null;
-        if (bgpPeers.containsKey(dstIpAddress)) {
-            //Route to a peer
-            log.debug("Route to peer {}", dstIpAddress);
-            BgpPeer peer = bgpPeers.get(dstIpAddress);
+
+        if (bgpPeers.containsKey(nextHopIpAddress)) {
+            // Route to a peer
+            log.debug("Route to peer {}", nextHopIpAddress);
+            BgpPeer peer = bgpPeers.get(nextHopIpAddress);
             egressInterface = interfaces.get(peer.getInterfaceName());
         } else {
-            //Route to non-peer
-            log.debug("Route to non-peer {}", dstIpAddress);
-            egressInterface = interfacePtree.match(
-                    new Prefix(dstIpAddress.getAddress(), 32));
+            // Route to non-peer
+            log.debug("Route to non-peer {}", nextHopIpAddress);
+            egressInterface = getOutgoingInterface(nextHopIpAddress);
             if (egressInterface == null) {
-                log.warn("No outgoing interface found for {}", dstIpAddress.getHostAddress());
+                log.warn("No outgoing interface found for {}", nextHopIpAddress
+                        .getHostAddress());
                 return;
             }
         }
 
+        // See if we know the MAC address of the next hop
+        MACAddress nextHopMacAddress = proxyArp.getMacAddress(nextHopIpAddress);
+
         if (nextHopMacAddress == null) {
-            prefixesWaitingOnArp.put(dstIpAddress,
+            prefixesWaitingOnArp.put(nextHopIpAddress,
                     new RibUpdate(Operation.UPDATE, prefix, rib));
-            proxyArp.sendArpRequest(dstIpAddress, this, true);
+            proxyArp.sendArpRequest(nextHopIpAddress, this, true);
             return;
         } else {
-            if (!bgpPeers.containsKey(dstIpAddress)) {
-                //If the prefix is for a non-peer we need to ensure there's a path,
-                //and push one if there isn't.
-                Path path = pushedPaths.get(dstIpAddress);
-                if (path == null) {
-                    path = new Path(egressInterface, dstIpAddress);
-                    calculateAndPushPath(path, nextHopMacAddress);
-                    pushedPaths.put(dstIpAddress, path);
-                }
 
-                path.incrementUsers();
-                prefixToPath.put(prefix, path);
-            }
+            //For all prefixes we need to add a intent for each of them
+            addRouteIntent(prefix, egressInterface, nextHopMacAddress);
 
-            //For all prefixes we need to add the first-hop mac-rewriting flows
-            addPrefixFlows(prefix, egressInterface, nextHopMacAddress);
         }
-        */
+
     }
 
     /**
-     * Add a flow to match dst-IP prefix and rewrite MAC for one IP prefix to
-     * all other border switches.
+     * Install a flow intent for a prefix.
+     * Intent will match dst IP prefix and rewrite dst MAC address at all other
+     * border switches, then forward packets according to dst MAC address.
+     *
+     * @param prefix IP prefix from BGP route
+     * @param egressInterface egress Interface connected to next hop router
+     * @param nextHopMacAddress MAC address of next hop router
      */
-    private void addPrefixFlows(Prefix prefix, Interface egressInterface,
+    private void addRouteIntent(Prefix prefix, Interface egressInterface,
             MACAddress nextHopMacAddress) {
-        log.debug("Adding flows for prefix {}, next hop mac {}",
+        log.debug("Adding intent for prefix {}, next hop mac {}",
                 prefix, nextHopMacAddress);
 
-        /*
-        FlowPath flowPath = new FlowPath();
-        flowPath.setInstallerId(callerId);
+        MultiPointToSinglePointIntent pushedIntent = pushedRouteIntents.get(prefix);
 
-        // Set flowPath FlowPathType and FlowPathUserState
-        flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
-        flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
+        // Just for testing.
+        if (pushedIntent != null) {
+            log.error("There should not be a pushed intent: {}", pushedIntent);
+        }
 
-        // Insert dst-ip prefix based forwarding and MAC rewrite flow entry
-        // only to the first-host switches
-        FlowPathFlags flowPathFlags = new FlowPathFlags();
-        flowPathFlags.setFlags(FlowPathFlags.KEEP_ONLY_FIRST_HOP_ENTRY);
-        flowPath.setFlowPathFlags(flowPathFlags);
+        SwitchPort egressPort = egressInterface.getSwitchPort();
 
-        // Create the DataPath object: dstSwitchPort
-        SwitchPort dstPort =
-                new SwitchPort(new Dpid(egressInterface.getDpid()),
-                        new PortNumber(egressInterface.getPort()));
+        Set<SwitchPort> ingressPorts = new HashSet<SwitchPort>();
 
-        // We only need one flow mod per switch, so pick one interface on each
-        // switch
-        Map<Long, Interface> srcInterfaces = new HashMap<>();
         for (Interface intf : interfaces.values()) {
-            if (!srcInterfaces.containsKey(intf.getDpid())
-                    && !intf.equals(egressInterface)) {
-                srcInterfaces.put(intf.getDpid(), intf);
+            if (!intf.equals(egressInterface)) {
+                SwitchPort srcPort = intf.getSwitchPort();
+                ingressPorts.add(srcPort);
             }
         }
-        for (Interface srcInterface : srcInterfaces.values()) {
 
-            if (egressInterface.equals(srcInterface)) {
-                continue;
-            }
+        // Match the destination IP prefix at the first hop
+        PacketMatchBuilder builder = new PacketMatchBuilder();
+        builder.setDstIp(new IPv4(InetAddresses
+                .coerceToInteger(prefix.getInetAddress())),
+                (short) prefix.getPrefixLength());
+        PacketMatch packetMatch = builder.build();
 
-            // Create flowPath FlowId
-            flowPath.setFlowId(new FlowId());
+        // Rewrite the destination MAC address
+        ModifyDstMacAction modifyDstMacAction =
+                new ModifyDstMacAction(nextHopMacAddress);
 
-            // Create DataPath object: srcSwitchPort
-            SwitchPort srcPort =
-                    new SwitchPort(new Dpid(srcInterface.getDpid()),
-                            new PortNumber(srcInterface.getPort()));
+        MultiPointToSinglePointIntent intent =
+                new MultiPointToSinglePointIntent(intentIdGenerator.getNewId(),
+                        packetMatch, modifyDstMacAction, ingressPorts, egressPort);
 
-            DataPath dataPath = new DataPath();
-            dataPath.setSrcPort(srcPort);
-            dataPath.setDstPort(dstPort);
-            flowPath.setDataPath(dataPath);
+        intentService.submit(intent);
 
-            // Create flow path matching condition(s): IPv4 Prefix
-            FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
-            flowEntryMatch.enableEthernetFrameType(Ethernet.TYPE_IPV4);
-            IPv4Net dstIPv4Net = new IPv4Net(prefix.toString());
-            flowEntryMatch.enableDstIPv4Net(dstIPv4Net);
-            flowPath.setFlowEntryMatch(flowEntryMatch);
-
-             *
-             * Create the Flow Entry Action(s): dst-MAC rewrite action
-             *
-            FlowEntryActions flowEntryActions = new FlowEntryActions();
-            FlowEntryAction flowEntryAction1 = new FlowEntryAction();
-            flowEntryAction1.setActionSetEthernetDstAddr(nextHopMacAddress);
-            // flowEntryAction1.actionSetEthernetDstAddr(nextHopMacAddress);
-            flowEntryActions.addAction(flowEntryAction1);
-            flowPath.setFlowEntryActions(flowEntryActions);
-
-            // Flow Path installation, only to first hop switches
-            // TODO: Add the flow by using the new Path Intent framework
-            /*
-            if (flowManagerService.addFlow(flowPath) == null) {
-                log.error("Failed to install flow path to the first hop for " +
-                        "prefix: {}, nextHopMacAddress: {}", prefix.getAddress(),
-                        nextHopMacAddress);
-            }
-            else {
-                log.debug("Successfully installed flow path to the first hop " +
-                        "for prefix: {}, nextHopMacAddress: {}", prefix.getAddress(),
-                        nextHopMacAddress);
-
-                pushedFlowIds.put(prefix, flowPath.flowId());
-            }
-
-        }
-        */
+        // Maintain the Intent
+        pushedRouteIntents.put(prefix, intent);
     }
 
+    /**
+     * Remove prefix from InvertedRadixTree, if success, then try to delete the
+     * relative intent.
+     *
+     * @param update RIB update
+     */
     private void processRibDelete(RibUpdate update) {
         synchronized (this) {
             Prefix prefix = update.getPrefix();
 
             // if (ptree.remove(prefix, update.getRibEntry())) {
+
             // TODO check the change of logic here - remove doesn't check that
-            // the
-            // rib entry was what we expected (and we can't do this
+            // the rib entry was what we expected (and we can't do this
             // concurrently)
+
             if (bgpRoutes.remove(prefix.toBinaryString())) {
                 /*
                  * Only delete flows if an entry was actually removed from the tree.
                  * If no entry was removed, the <prefix, nexthop> wasn't there so
                  * it's probably already been removed and we don't need to do anything
                  */
-                executeDeletePrefix(prefix, update.getRibEntry());
+                executeDeleteRoute(prefix, update.getRibEntry());
+
             }
         }
     }
 
-    private void executeDeletePrefix(Prefix prefix, RibEntry ribEntry) {
-        deletePrefixFlows(prefix);
-
-        log.debug("Deleting {} to {}", prefix, ribEntry.getNextHop());
-
-        if (!bgpPeers.containsKey(ribEntry.getNextHop())) {
-            log.debug("Getting path for route with non-peer nexthop");
-            Path path = prefixToPath.remove(prefix);
-
-            if (path != null) {
-                // path could be null if we added to the Ptree but didn't push
-                // flows yet because we were waiting to resolve ARP
-
-                path.decrementUsers();
-                if (path.getUsers() <= 0 && !path.isPermanent()) {
-                    deletePath(path);
-                    pushedPaths.remove(path.getDstIpAddress());
-                }
-            }
-        }
-    }
-
-    // TODO have not tested this module
-    private void deletePrefixFlows(Prefix prefix) {
-        log.debug("Deleting flows for prefix {}", prefix);
-
-        //
-        // TODO: Delete the flow by using the new Path Intent framework
-        // NOTE: During the refactoring of the code below, if obtaining
-        // the values of the removed flowIds is needed, the first
-        // removeAll() statement should be replaced with the second removeAll()
-        // statement.
-        //
-        // pushedFlowIds.removeAll(prefix);
-        /*
-        Collection<FlowId> flowIds = pushedFlowIds.removeAll(prefix);
-        for (FlowId flowId : flowIds) {
-            if (log.isTraceEnabled()) {
-                //Trace the flow status by flowPath in the switch before deleting it
-                log.trace("Pushing a DELETE flow mod to flowPath : {}",
-                        flowManagerService.getFlow(flowId).toString());
-            }
-
-            if( flowManagerService.deleteFlow(flowId))
-            {
-                log.debug("Successfully deleted FlowId: {}",flowId);
-            }
-            else
-            {
-                log.debug("Failed to delete FlowId: {}",flowId);
-            }
-        }
-        */
-    }
-
-    // TODO need to record the path and then delete here
-    private void deletePath(Path path) {
-        log.debug("Deleting flows for path to {}",
-                path.getDstIpAddress().getHostAddress());
-
-        // TODO need update
-        /*for (PushedFlowMod pfm : path.getFlowMods()) {
-            if (log.isTraceEnabled()) {
-                log.trace("Pushing a DELETE flow mod to {}, dst MAC {}",
-                        new Object[] {HexString.toHexString(pfm.getDpid()),
-                        HexString.toHexString(pfm.getFlowMod().getMatch().getDataLayerDestination())
-                });
-            }
-
-            sendDeleteFlowMod(pfm.getFlowMod(), pfm.getDpid());
-        }*/
-    }
-
-    // TODO test next-hop changes
-    // TODO check delete/add synchronization
-
     /**
-     * On startup, we need to calculate a full mesh of paths between all gateway
-     * switches.
+     * Delete prefix intent installed.
+     *
+     * @param prefix IP prefix withdrew in a rib update announcement
+     * @param ribEntry next hop information
      */
-    private void setupFullMesh() {
-        // TODO: Fix the code below. Note that "deviceStorage" was removed.
+    private void executeDeleteRoute(Prefix prefix, RibEntry ribEntry) {
+        log.debug("Deleting {} to {}", prefix, ribEntry.getNextHop());
 
-        /*
+        MultiPointToSinglePointIntent intent = pushedRouteIntents.remove(prefix);
 
-        //For each border router, calculate and install a path from every other
-        //border switch to said border router. However, don't install the entry
-        //in to the first hop switch, as we need to install an entry to rewrite
-        //for each prefix received. This will be done later when prefixes have
-        //actually been received.
-
-        for (BgpPeer peer : bgpPeers.values()) {
-            Interface peerInterface = interfaces.get(peer.getInterfaceName());
-
-            //We know there's not already a Path here pushed, because this is
-            //called before all other routing
-            Path path = new Path(peerInterface, peer.getIpAddress());
-            path.setPermanent();
-
-            //See if we know the MAC address of the peer. If not we can't
-            //do anything until we learn it
-            MACAddress macAddress = null;
-            IDeviceObject nextHopDevice =
-                    deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(peer.getIpAddress()));
-
-            if(nextHopDevice == null){
-                log.debug("There is no DeviceObject for {}", peer.getIpAddress().getHostAddress());
-                //Put in the pending paths list first
-                pathsWaitingOnArp.put(peer.getIpAddress(), path);
-                proxyArp.sendArpRequest(peer.getIpAddress(), this, true);
-                continue;
-            }
-
-            macAddress = MACAddress.valueOf(nextHopDevice.getMACAddress());
-
-            if (macAddress == null) {
-                log.debug("Don't know MAC for {}", peer.getIpAddress().getHostAddress());
-                //Put in the pending paths list first
-                pathsWaitingOnArp.put(peer.getIpAddress(), path);
-                proxyArp.sendArpRequest(peer.getIpAddress(), this, true);
-                continue;
-            }
-
-            //If we know the MAC, lets go ahead and push the paths to this peer
-            calculateAndPushPath(path, macAddress);
+        if (intent == null) {
+            log.debug("There is no intent in pushedRouteIntents to delete for " +
+                    "prefix: {}", prefix);
+        } else {
+            intentService.withdraw(intent);
+            log.debug("Deleted the pushedRouteIntent for prefix: {}", prefix);
         }
-        */
-    }
-
-    private void calculateAndPushPath(Path path, MACAddress dstMacAddress) {
-        // Interface dstInterface = path.getDstInterface();
-
-        log.debug("Setting up path to {}, {}", path.getDstIpAddress().getHostAddress(),
-                dstMacAddress);
-
-        /*
-        FlowPath flowPath = new FlowPath();
-
-        flowPath.setInstallerId(callerId);
-
-        // Set flowPath FlowPathType and FlowPathUserState
-        flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
-        flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
-
-        // Insert the dest-mac based forwarding flow entry to the non-first-hop
-        // switches
-        FlowPathFlags flowPathFlags = new FlowPathFlags();
-        flowPathFlags.setFlags(FlowPathFlags.DISCARD_FIRST_HOP_ENTRY);
-        flowPath.setFlowPathFlags(flowPathFlags);
-
-        // Create the DataPath object: dstSwitchPort
-        SwitchPort dstPort =
-                new SwitchPort(new Dpid(dstInterface.getDpid()),
-                        new PortNumber(dstInterface.getPort()));
-
-        for (Interface srcInterface : interfaces.values()) {
-
-            if (dstInterface.equals(srcInterface)) {
-                continue;
-            }
-
-            // Create flowPath FlowId
-            flowPath.setFlowId(new FlowId());
-
-            // Create the DataPath object: srcSwitchPort
-            SwitchPort srcPort =
-                    new SwitchPort(new Dpid(srcInterface.getDpid()),
-                            new PortNumber(srcInterface.getPort()));
-
-            DataPath dataPath = new DataPath();
-            dataPath.setSrcPort(srcPort);
-            dataPath.setDstPort(dstPort);
-            flowPath.setDataPath(dataPath);
-
-            // Create the Flow Path Match condition(s)
-            FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
-            flowEntryMatch.enableEthernetFrameType(Ethernet.TYPE_IPV4);
-            flowEntryMatch.enableDstMac(dstMacAddress);
-            flowPath.setFlowEntryMatch(flowEntryMatch);
-
-            // NOTE: No need to add ACTION_OUTPUT. It is implied when creating
-            // Shortest Path Flow, and is always the last action for the Flow
-            // Entries
-            log.debug("FlowPath of MAC based forwarding: {}", flowPath.toString());
-            // TODO: Add the flow by using the new Path Intent framework
-
-            if (flowManagerService.addFlow(flowPath) == null) {
-                log.error("Failed to set up MAC based forwarding path to {}, {}",
-                        path.getDstIpAddress().getHostAddress(),dstMacAddress);
-            }
-            else {
-                log.debug("Successfully set up MAC based forwarding path to {}, {}",
-                        path.getDstIpAddress().getHostAddress(),dstMacAddress);
-            }
-
-        }
-        */
-    }
-
-    @Override
-    public void beginRoutingNew() {
-        setupBgpPathsNew();
-
-        // setupFullMesh();
-
-        // Suppress link discovery on external-facing router ports
-
-        for (Interface intf : interfaces.values()) {
-            linkDiscoveryService.disableDiscoveryOnPort(intf.getDpid(), intf.getPort());
-        }
-
-        bgpUpdatesExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                doUpdatesThread();
-            }
-        });
     }
 
     /**
@@ -847,7 +546,7 @@
      * srcPort, long srcMac, int srcIP, long dstSwitch, long dstPort, long
      * dstMac, int dstIP
      */
-    private void setupBgpPathsNew() {
+    private void setupBgpPaths() {
         IntentOperationList operations = new IntentOperationList();
         for (BgpPeer bgpPeer : bgpPeers.values()) {
             Interface peerInterface = interfaces.get(bgpPeer.getInterfaceName());
@@ -861,9 +560,14 @@
             SwitchPort srcPort =
                     new SwitchPort(bgpdAttachmentPoint.getDpid(),
                             bgpdAttachmentPoint.getPortNumber());
+            // TODO: replace the code below with peerInterface.getSwitchPort()
+            // when using poingToPointIntent
             SwitchPort dstPort =
                     new SwitchPort(new Dpid(peerInterface.getDpid()),
                             new PortNumber(peerInterface.getSwitchPort().getPortNumber()));
+
+            // TODO: add TCP port number 179 into intent for BGP
+
             ShortestPathIntent fwdIntent = new ShortestPathIntent(fwdIntentId,
                     srcPort.getDpid().value(), srcPort.getPortNumber().value(),
                     ShortestPathIntent.EMPTYMACADDRESS, srcIP,
@@ -881,41 +585,26 @@
         pathRuntime.executeIntentOperations(operations);
     }
 
+    /**
+     * This method handles the prefixes which are waiting for ARP replies for
+     * MAC addresses of next hops.
+     *
+     * @param ipAddress next hop router IP address, for which we sent ARP request out
+     * @param macAddress MAC address which is relative to the ipAddress
+     */
     @Override
     public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
         log.debug("Received ARP response: {} => {}",
                 ipAddress.getHostAddress(), macAddress);
 
         /*
-         * We synchronize on this to prevent changes to the ptree while we're pushing
-         * flows to the switches. If the ptree changes, the ptree and switches
-         * could get out of sync.
+         * We synchronize on this to prevent changes to the InvertedRadixTree
+         * while we're pushing intent. If the InvertedRadixTree changes, the
+         * InvertedRadixTree and intent could get out of sync.
          */
         synchronized (this) {
-            Path path = pathsWaitingOnArp.remove(ipAddress);
 
-            if (path != null) {
-                log.debug("Pushing path to {} at {} on {}",
-                        path.getDstIpAddress().getHostAddress(), macAddress,
-                        path.getDstInterface().getSwitchPort());
-                // These paths should always be to BGP peers. Paths to non-peers
-                // are
-                // handled once the first prefix is ready to push
-                if (pushedPaths.containsKey(path.getDstIpAddress())) {
-                    // A path already got pushed to this endpoint while we were
-                    // waiting
-                    // for ARP. We'll copy over the permanent attribute if it is
-                    // set on this path.
-                    if (path.isPermanent()) {
-                        pushedPaths.get(path.getDstIpAddress()).setPermanent();
-                    }
-                } else {
-                    calculateAndPushPath(path, macAddress);
-                    pushedPaths.put(path.getDstIpAddress(), path);
-                }
-            }
-
-            Set<RibUpdate> prefixesToPush = prefixesWaitingOnArp.removeAll(ipAddress);
+          Set<RibUpdate> prefixesToPush = prefixesWaitingOnArp.removeAll(ipAddress);
 
             for (RibUpdate update : prefixesToPush) {
                 // These will always be adds
@@ -926,22 +615,20 @@
                     log.debug("Pushing prefix {} next hop {}", update.getPrefix(),
                             rib.getNextHop().getHostAddress());
                     // We only push prefix flows if the prefix is still in the
-                    // ptree
-                    // and the next hop is the same as our update. The prefix
-                    // could
-                    // have been removed while we were waiting for the ARP, or
-                    // the
-                    // next hop could have changed.
+                    // InvertedRadixTree and the next hop is the same as our update.
+                    // The prefix could have been removed while we were waiting
+                    // for the ARP, or the next hop could have changed.
                     executeRibAdd(update);
                 } else {
-                    log.debug("Received ARP response, but {},{} is no longer in ptree",
-                            update.getPrefix(), update.getRibEntry());
+                    log.debug("Received ARP response, but {},{} is no longer in " +
+                            "InvertedRadixTree", update.getPrefix(),
+                            update.getRibEntry());
                 }
             }
         }
     }
 
-    // XXX OpenFlow message classes have been removed
+
     /*private void setupArpFlows() {
         OFMatch match = new OFMatch();
         match.setDataLayerType(Ethernet.TYPE_ARP);
@@ -1027,17 +714,18 @@
         }
     }*/
 
-    private void beginRouting() {
+    /**
+     * The SDN-IP application is started from this method.
+     */
+    @Override
+    public void beginRouting() {
         log.debug("Topology is now ready, beginning routing function");
-        // TODO: Fix for the new Topology Network Graph
-        // topology = topologyNetService.newDatabaseTopology();
 
-        // Wait Pavlin's API. We need the following functions.
+        // TODO
         /*setupArpFlows();
         setupDefaultDropFlows();*/
 
-        //setupBgpPaths();
-        setupFullMesh();
+        setupBgpPaths();
 
         // Suppress link discovery on external-facing router ports
         for (Interface intf : interfaces.values()) {
@@ -1052,69 +740,9 @@
         });
     }
 
-    // Before inserting the paths for BGP traffic, we should check whether
-    // all the switches in the configuration file are discovered by ONOS
-    private void checkSwitchesConnected() {
-        // TODO: Fix the code below after topoSwitchSerice was removed
-        /*
-        for (String dpid : switches) {
-
-            Iterator<ISwitchObject> activeSwitches = topoSwitchService.
-                    getActiveSwitches().iterator();
-            while(activeSwitches.hasNext())
-            {
-                ISwitchObject switchObject = activeSwitches.next();
-                if (switchObject.getDPID().equals(dpid)) {
-                    break;
-                }
-                if(activeSwitches.hasNext() == false) {
-                    log.debug("Not all switches are here yet");
-                    return;
-                }
-            }
-        }
-        switchesConnected = true;
-        */
-    }
-
-    // Actually we only need to go half way round to verify full mesh
-    // connectivity
-    private void checkTopologyReady() {
-        for (Interface dstInterface : interfaces.values()) {
-            for (Interface srcInterface : interfaces.values()) {
-                if (dstInterface.equals(srcInterface)) {
-                    continue;
-                }
-
-                // TODO: Fix for the new Topology Network Graph
-                /*
-                DataPath shortestPath = topologyNetService.getDatabaseShortestPath(
-                        srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
-
-                if (shortestPath == null){
-                    log.debug("Shortest path between {} and {} not found",
-                            srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
-                    return;
-                }
-                */
-            }
-        }
-        topologyReady = true;
-    }
-
-    private void checkStatus() {
-        if (!switchesConnected) {
-            checkSwitchesConnected();
-        }
-        boolean oldTopologyReadyStatus = topologyReady;
-        if (switchesConnected && !topologyReady) {
-            checkTopologyReady();
-        }
-        if (!oldTopologyReadyStatus && topologyReady) {
-            beginRouting();
-        }
-    }
-
+    /**
+     * Thread for handling RIB updates.
+     */
     private void doUpdatesThread() {
         boolean interrupted = false;
         try {
@@ -1156,6 +784,12 @@
         }
     }
 
+    /**
+     * Judge whether a RIB update is in correct order.
+     *
+     * @param update RIB update
+     * @return boolean whether the RIB update is in in correct order
+     */
     private boolean validateUpdate(RibUpdate update) {
         RibEntry newEntry = update.getRibEntry();
         RibEntry oldEntry = bgpRoutes.getValueForExactKey(
@@ -1184,6 +818,13 @@
                 newEntry.getSequenceNum() > oldEntry.getSequenceNum();
     }
 
+    /**
+     * To find the Interface which has longest matchable IP prefix (sub-network
+     *  prefix) to next hop IP address.
+     *
+     * @param address the IP address of next hop router
+     * @return Interface the Interface which has longest matchable IP prefix
+     */
     private Interface longestInterfacePrefixMatch(InetAddress address) {
         Prefix prefixToSearchFor = new Prefix(address.getAddress(),
                 Prefix.MAX_PREFIX_LENGTH);
@@ -1199,90 +840,6 @@
         return intf;
     }
 
-    // The code below should be reimplemented after removal of Floodlight's
-    // ITopologyService API. It should be implemented on top of network graph
-    // notifications. (It was pretty hacky anyway...)
-    /*
-    @Override
-    public void topologyChanged() {
-        if (topologyReady) {
-            return;
-        }
-
-        boolean refreshNeeded = false;
-        for (LDUpdate ldu : topologyService.getLastLinkUpdates()){
-            if (!ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_UPDATED)){
-                //We don't need to recalculate anything for just link updates
-                //They happen very frequently
-                refreshNeeded = true;
-            }
-
-            log.debug("Topo change {}", ldu.getOperation());
-
-            if (ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_ADDED)){
-                synchronized (linkUpdates) {
-                    linkUpdates.add(ldu);
-                }
-            }
-        }
-
-        if (refreshNeeded && !topologyReady){
-            topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
-        }
-    }
-    */
-
-    // ******************
-    // IOFSwitchListener
-    // ******************
-
-    @Override
-    public void switchActivatedMaster(long swId) {
-        IOFSwitch sw = floodlightProvider.getSwitch(swId);
-        if (sw == null) {
-            log.warn("Added switch not available {} ", swId);
-            return;
-        }
-        if (!topologyReady) {
-            sw.clearAllFlowMods();
-        }
-
-        //flowCache.switchConnected(sw);
-    }
-
-    @Override
-    public void switchActivatedEqual(long swId) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void switchMasterToEqual(long swId) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void switchEqualToMaster(long swId) {
-        // for now treat as switchActivatedMaster
-        switchActivatedMaster(swId);
-    }
-
-    @Override
-    public void switchDisconnected(long swId) {
-        // Not used
-    }
-
-    @Override
-    public void switchPortChanged(long swId, OFPortDesc port, PortChangeType pct) {
-        // Not used
-    }
-
-    @Override
-    public String getName() {
-        return "sdnip";
-    }
-
     /*
      * IConfigInfoService methods
      */
@@ -1309,6 +866,11 @@
         return false;
     }
 
+    /**
+     * To find the relative egress Interface for a next hop IP address.
+     *
+     * @param dstIpAddress the IP address of next hop router
+     */
     @Override
     public Interface getOutgoingInterface(InetAddress dstIpAddress) {
         return longestInterfacePrefixMatch(dstIpAddress);
@@ -1328,4 +890,5 @@
     public short getVlan() {
         return vlan;
     }
+
 }
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/web/SdnIpSetup.java b/src/main/java/net/onrc/onos/apps/sdnip/web/SdnIpSetup.java
index d1c4605..4bed425 100644
--- a/src/main/java/net/onrc/onos/apps/sdnip/web/SdnIpSetup.java
+++ b/src/main/java/net/onrc/onos/apps/sdnip/web/SdnIpSetup.java
@@ -13,7 +13,7 @@
     public String sdnipSetupMethod() {
         ISdnIpService sdnIp = (ISdnIpService) getContext()
                               .getAttributes().get(ISdnIpService.class.getCanonicalName());
-        sdnIp.beginRoutingNew();
+        sdnIp.beginRouting();
         return "SdnIp SetupBgpPaths Succeeded";
     }
 
diff --git a/src/test/java/net/onrc/onos/apps/sdnip/SdnIpTest.java b/src/test/java/net/onrc/onos/apps/sdnip/SdnIpTest.java
new file mode 100644
index 0000000..f5a6d31
--- /dev/null
+++ b/src/test/java/net/onrc/onos/apps/sdnip/SdnIpTest.java
@@ -0,0 +1,81 @@
+package net.onrc.onos.apps.sdnip;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
+import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
+import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
+
+public class SdnIpTest {
+
+    private Map<String, Interface> interfaces;
+    private InvertedRadixTree<Interface> interfaceRoutes;
+
+    private Interface longestInterfacePrefixMatch(InetAddress address) {
+        Prefix prefixToSearchFor = new Prefix(address.getAddress(),
+                Prefix.MAX_PREFIX_LENGTH);
+        Iterator<Interface> it =
+                interfaceRoutes.getValuesForKeysPrefixing(
+                        prefixToSearchFor.toBinaryString()).iterator();
+        Interface intf = null;
+        // Find the last prefix, which will be the longest prefix
+        while (it.hasNext()) {
+            intf = it.next();
+        }
+
+        return intf;
+    }
+    /**
+     * This is just a test of the InvertedRadixTree, rather than an actual unit
+     * test of SdnIp.
+     *
+     * @throws IOException
+     */
+    @Test
+    public void getOutgoingInterfaceTest() throws IOException {
+
+        interfaces = new HashMap<>();
+        interfaceRoutes = new ConcurrentInvertedRadixTree<>(
+                new DefaultByteArrayNodeFactory());
+
+        Interface interface1 = new Interface("sw3-eth1", "00:00:00:00:00:00:00:a3",
+                (short) 1, "192.168.10.101", 24);
+        interfaces.put(interface1.getName(), interface1);
+        Interface interface2 = new Interface("sw5-eth1", "00:00:00:00:00:00:00:a5",
+                (short) 1, "192.168.20.101", 16);
+        interfaces.put(interface2.getName(), interface2);
+        Interface interface3 = new Interface("sw2-eth1", "00:00:00:00:00:00:00:a2",
+                (short) 1, "192.168.60.101", 16);
+        interfaces.put(interface3.getName(), interface3);
+        Interface interface4 = new Interface("sw6-eth1", "00:00:00:00:00:00:00:a6",
+                (short) 1, "192.168.60.101", 30);
+        interfaces.put(interface4.getName(), interface4);
+        Interface interface5 = new Interface("sw4-eth4", "00:00:00:00:00:00:00:a4",
+                (short) 4, "192.168.60.101", 24);
+        interfaces.put(interface5.getName(), interface5);
+
+        for (Interface intf : interfaces.values()) {
+            Prefix prefix = new Prefix(intf.getIpAddress().getAddress(),
+                    intf.getPrefixLength());
+            interfaceRoutes.put(prefix.toBinaryString(), intf);
+        }
+
+        // Check whether the prefix length takes effect
+        InetAddress nextHopAddress = InetAddress.getByName("192.0.0.1");
+        assertNotNull(nextHopAddress);
+        assertNull(longestInterfacePrefixMatch(nextHopAddress));
+
+        // Check whether it returns the longest matchable address
+        nextHopAddress = InetAddress.getByName("192.168.60.101");
+        assertEquals("sw6-eth1", longestInterfacePrefixMatch(nextHopAddress).getName());
+
+    }
+}