Moved BGP code and Router code into their own bundle.

The main goal of this is to allow routing code to be used by multiple
applications.

Changes include:
 * Created an onos-app-routing bundle and moved BGP code and routing code
   into it.
 * Created an onos-app-routing-api bundle as a common API bundle between
   onos-app-routing and onos-app-sdnip, to prevent circular dependencies.
 * Moved API classes into onos-app-routing-api bundle.
 * Made Router and BgpSessionManager into OSGi components. This is not quite
   clean, because there is still a chain of start() method calls from SdnIp
   through to BgpSessionManager to preserve startup order. This should be
   revisted so components can be started using activate()
 * Created BgpService and RoutingService APIs to glue different components
   together.
 * Many unit test changes. A lot of the previous unit tests spanned the
   Router and IntentSynchronizer classes, but this is not possible any more
   since these classes are in different bundles. I had to rewrite some of
   these tests so that each unit test class only tests one real class. A
   nice side-effect is that the tests are now simpler because each test
   tests less functionality.
 * Removed SdnIp test seeing as it doesn't run automatically, was already
   broken and has been largely superseded by other unit tests and the nightly
   functional tests.

Change-Id: I70ecf5391aa353e99e7cdcf7ed38a530c87571bb
diff --git a/apps/pom.xml b/apps/pom.xml
index ca64e5f..2746e65 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -46,6 +46,8 @@
         <module>oecfg</module>
         <module>demo</module>
         <module>election</module>
+        <module>routing</module>
+        <module>routing-api</module>
     </modules>
 
     <properties>
diff --git a/apps/routing-api/pom.xml b/apps/routing-api/pom.xml
new file mode 100644
index 0000000..c83cb8f
--- /dev/null
+++ b/apps/routing-api/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>onos-apps</artifactId>
+        <groupId>org.onosproject</groupId>
+        <version>1.1.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>onos-app-routing-api</artifactId>
+
+    <packaging>bundle</packaging>
+    <description>API for routing applications</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/apps/routing-api/src/main/java/org/onosproject/routingapi/BgpService.java b/apps/routing-api/src/main/java/org/onosproject/routingapi/BgpService.java
new file mode 100644
index 0000000..c7e58d4
--- /dev/null
+++ b/apps/routing-api/src/main/java/org/onosproject/routingapi/BgpService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.routingapi;
+
+/**
+ * Provides a way of interacting with the BGP protocol component.
+ */
+public interface BgpService {
+
+    /**
+     * Starts the BGP service.
+     *
+     * @param routeListener listener to send route updates to
+     * @param bgpPort port number to listen on
+     */
+    void start(RouteListener routeListener, int bgpPort);
+
+    /**
+     * Stops the BGP service.
+     */
+    void stop();
+}
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/FibEntry.java b/apps/routing-api/src/main/java/org/onosproject/routingapi/FibEntry.java
similarity index 68%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/FibEntry.java
rename to apps/routing-api/src/main/java/org/onosproject/routingapi/FibEntry.java
index 988b49a..86b40f8 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/FibEntry.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routingapi/FibEntry.java
@@ -13,12 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip;
+package org.onosproject.routingapi;
 
+import com.google.common.base.MoreObjects;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 
+import java.util.Objects;
+
 /**
  * An entry in the Forwarding Information Base (FIB).
  */
@@ -67,4 +70,31 @@
     public MacAddress nextHopMac() {
         return nextHopMac;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof FibEntry)) {
+            return false;
+        }
+
+        FibEntry that = (FibEntry) o;
+
+        return Objects.equals(this.prefix, that.prefix) &&
+                Objects.equals(this.nextHopIp, that.nextHopIp) &&
+                Objects.equals(this.nextHopMac, that.nextHopMac);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(prefix, nextHopIp, nextHopMac);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("prefix", prefix)
+                .add("nextHopIp", nextHopIp)
+                .add("nextHopMac", nextHopMac)
+                .toString();
+    }
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/FibListener.java b/apps/routing-api/src/main/java/org/onosproject/routingapi/FibListener.java
similarity index 96%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/FibListener.java
rename to apps/routing-api/src/main/java/org/onosproject/routingapi/FibListener.java
index 13674e3..218139e 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/FibListener.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routingapi/FibListener.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip;
+package org.onosproject.routingapi;
 
 import java.util.Collection;
 
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/FibUpdate.java b/apps/routing-api/src/main/java/org/onosproject/routingapi/FibUpdate.java
similarity index 70%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/FibUpdate.java
rename to apps/routing-api/src/main/java/org/onosproject/routingapi/FibUpdate.java
index a3d8c3c..2b0215c 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/FibUpdate.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routingapi/FibUpdate.java
@@ -13,7 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip;
+package org.onosproject.routingapi;
+
+import com.google.common.base.MoreObjects;
+
+import java.util.Objects;
 
 /**
  * Represents a change to the Forwarding Information Base (FIB).
@@ -66,4 +70,29 @@
     public FibEntry entry() {
         return entry;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof FibUpdate)) {
+            return false;
+        }
+
+        FibUpdate that = (FibUpdate) o;
+
+        return Objects.equals(this.type, that.type) &&
+                Objects.equals(this.entry, that.entry);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, entry);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("type", type)
+                .add("entry", entry)
+                .toString();
+    }
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/RouteEntry.java b/apps/routing-api/src/main/java/org/onosproject/routingapi/RouteEntry.java
similarity index 96%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/RouteEntry.java
rename to apps/routing-api/src/main/java/org/onosproject/routingapi/RouteEntry.java
index 3cffb7e..3e861e5 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/RouteEntry.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routingapi/RouteEntry.java
@@ -13,16 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip;
+package org.onosproject.routingapi;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Objects;
-
+import com.google.common.base.MoreObjects;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 
-import com.google.common.base.MoreObjects;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * Represents a route entry for an IP prefix.
@@ -77,7 +76,7 @@
      * @param ipPrefix the IP prefix to use
      * @return the binary string representation
      */
-    static String createBinaryString(IpPrefix ipPrefix) {
+    public static String createBinaryString(IpPrefix ipPrefix) {
         if (ipPrefix.prefixLength() == 0) {
             return "";
         }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/RouteListener.java b/apps/routing-api/src/main/java/org/onosproject/routingapi/RouteListener.java
similarity index 95%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/RouteListener.java
rename to apps/routing-api/src/main/java/org/onosproject/routingapi/RouteListener.java
index 2d9ddc9..55fa70d 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/RouteListener.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routingapi/RouteListener.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip;
+package org.onosproject.routingapi;
 
 import java.util.Collection;
 
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/RouteUpdate.java b/apps/routing-api/src/main/java/org/onosproject/routingapi/RouteUpdate.java
similarity index 98%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/RouteUpdate.java
rename to apps/routing-api/src/main/java/org/onosproject/routingapi/RouteUpdate.java
index f63f214..47aa3c2 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/RouteUpdate.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routingapi/RouteUpdate.java
@@ -13,13 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip;
+package org.onosproject.routingapi;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.base.MoreObjects;
 
 import java.util.Objects;
 
-import com.google.common.base.MoreObjects;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * Represents a change in routing information.
diff --git a/apps/routing-api/src/main/java/org/onosproject/routingapi/RoutingService.java b/apps/routing-api/src/main/java/org/onosproject/routingapi/RoutingService.java
new file mode 100644
index 0000000..e0f9383
--- /dev/null
+++ b/apps/routing-api/src/main/java/org/onosproject/routingapi/RoutingService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.routingapi;
+
+import java.util.Collection;
+
+/**
+ * Provides a way of interacting with the RIB management component.
+ */
+public interface RoutingService {
+
+    /**
+     * Starts the routing service.
+     *
+     * @param listener listener to send FIB updates to
+     */
+    public void start(FibListener listener);
+
+    /**
+     * Stops the routing service.
+     */
+    public void stop();
+
+    /**
+     * Gets all IPv4 routes known to SDN-IP.
+     *
+     * @return the SDN-IP IPv4 routes
+     */
+    public Collection<RouteEntry> getRoutes4();
+
+    /**
+     * Gets all IPv6 routes known to SDN-IP.
+     *
+     * @return the SDN-IP IPv6 routes
+     */
+    public Collection<RouteEntry> getRoutes6();
+}
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouteEntryTest.java b/apps/routing-api/src/test/java/org/onosproject/routingapi/RouteEntryTest.java
similarity index 95%
rename from apps/sdnip/src/test/java/org/onosproject/sdnip/RouteEntryTest.java
rename to apps/routing-api/src/test/java/org/onosproject/routingapi/RouteEntryTest.java
index b977917..acce45a 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouteEntryTest.java
+++ b/apps/routing-api/src/test/java/org/onosproject/routingapi/RouteEntryTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip;
+package org.onosproject.routingapi;
+
+import org.hamcrest.Matchers;
+import org.junit.Test;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertThat;
 
-import org.junit.Test;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
-
 /**
  * Unit tests for the RouteEntry class.
  */
@@ -139,8 +140,8 @@
         Ip4Address nextHop3 = Ip4Address.valueOf("5.6.7.9");      // Different
         RouteEntry routeEntry3 = new RouteEntry(prefix3, nextHop3);
 
-        assertThat(routeEntry1, is(not(routeEntry2)));
-        assertThat(routeEntry1, is(not(routeEntry3)));
+        assertThat(routeEntry1, Matchers.is(not(routeEntry2)));
+        assertThat(routeEntry1, Matchers.is(not(routeEntry3)));
     }
 
     /**
diff --git a/apps/routing/pom.xml b/apps/routing/pom.xml
new file mode 100644
index 0000000..8656ebb
--- /dev/null
+++ b/apps/routing/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>onos-apps</artifactId>
+        <groupId>org.onosproject</groupId>
+        <version>1.1.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>onos-app-routing</artifactId>
+
+    <packaging>bundle</packaging>
+    <description>Libraries for routing applications</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-app-routing-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+	<dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-thirdparty</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java b/apps/routing/src/main/java/org/onosproject/routing/Router.java
similarity index 87%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java
rename to apps/routing/src/main/java/org/onosproject/routing/Router.java
index d19f298..494c190 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/Router.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip;
+package org.onosproject.routing;
 
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimaps;
@@ -23,6 +23,12 @@
 import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
 import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
 import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
@@ -31,6 +37,14 @@
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostService;
+import org.onosproject.routingapi.BgpService;
+import org.onosproject.routingapi.FibEntry;
+import org.onosproject.routingapi.FibListener;
+import org.onosproject.routingapi.FibUpdate;
+import org.onosproject.routingapi.RouteEntry;
+import org.onosproject.routingapi.RouteListener;
+import org.onosproject.routingapi.RouteUpdate;
+import org.onosproject.routingapi.RoutingService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -47,12 +61,16 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.LinkedBlockingQueue;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * This class processes route updates and maintains a Routing Information Base
  * (RIB). After route updates have been processed and next hops have been
  * resolved, FIB updates are sent to any listening FIB components.
  */
-public class Router implements RouteListener {
+@Component(immediate = true)
+@Service
+public class Router implements RoutingService {
 
     private static final Logger log = LoggerFactory.getLogger(Router.class);
 
@@ -62,52 +80,52 @@
     private InvertedRadixTree<RouteEntry> ribTable6;
 
     // Stores all incoming route updates in a queue.
-    private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue;
+    private final BlockingQueue<Collection<RouteUpdate>> routeUpdatesQueue
+            = new LinkedBlockingQueue<>();
 
     // Next-hop IP address to route entry mapping for next hops pending MAC resolution
-    private final SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
+    private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
 
     // The IPv4 address to MAC address mapping
-    private final Map<IpAddress, MacAddress> ip2Mac;
+    private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>();
 
-    private final FibListener fibComponent;
-    private final HostService hostService;
-    private final ExecutorService bgpUpdatesExecutor;
-    private final HostListener hostListener;
+    private FibListener fibComponent;
 
-    /**
-     * Class constructor.
-     *
-     * @param fibComponent the intent synchronizer
-     * @param hostService       the host service
-     */
-    public Router(FibListener fibComponent, HostService hostService) {
-        // TODO move to a listener model for adding fib listeners
-        this.fibComponent = fibComponent;
-        this.hostService = hostService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
 
-        this.hostListener = new InternalHostListener();
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected BgpService bgpService;
 
+    private ExecutorService bgpUpdatesExecutor;
+    private final HostListener hostListener = new InternalHostListener();
+
+    @Activate
+    public void activate() {
         ribTable4 = new ConcurrentInvertedRadixTree<>(
                 new DefaultByteArrayNodeFactory());
         ribTable6 = new ConcurrentInvertedRadixTree<>(
                 new DefaultByteArrayNodeFactory());
-        routeUpdatesQueue = new LinkedBlockingQueue<>();
         routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
                 HashMultimap.<IpAddress, RouteEntry>create());
-        ip2Mac = new ConcurrentHashMap<>();
 
         bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
                 new ThreadFactoryBuilder()
                 .setNameFormat("sdnip-bgp-updates-%d").build());
     }
 
-    /**
-     * Starts the router.
-     */
-    public void start() {
+    @Deactivate
+    public void deactivate() {
+        log.debug("Stopped");
+    }
+
+    @Override
+    public void start(FibListener listener) {
+        this.fibComponent = checkNotNull(listener);
         this.hostService.addListener(hostListener);
 
+        bgpService.start(new InternalRouteListener(), 2000);
+
         bgpUpdatesExecutor.execute(new Runnable() {
             @Override
             public void run() {
@@ -116,10 +134,10 @@
         });
     }
 
-    /**
-     * Stops the router.
-     */
+    @Override
     public void stop() {
+        bgpService.stop();
+
         this.hostService.removeListener(hostListener);
 
         // Stop the thread(s)
@@ -137,8 +155,12 @@
         }
     }
 
-    @Override
-    public void update(Collection<RouteUpdate> routeUpdates) {
+    /**
+     * Entry point for route updates.
+     *
+     * @param routeUpdates collection of route updates to process
+     */
+    private void update(Collection<RouteUpdate> routeUpdates) {
         try {
             routeUpdatesQueue.put(routeUpdates);
         } catch (InterruptedException e) {
@@ -294,7 +316,9 @@
             withdrawPrefixes.forEach(p -> fibWithdraws.add(new FibUpdate(
                     FibUpdate.Type.DELETE, new FibEntry(p, null, null))));
 
-            fibComponent.update(fibUpdates, fibWithdraws);
+            if (!fibUpdates.isEmpty() || !fibWithdraws.isEmpty()) {
+                fibComponent.update(fibUpdates, fibWithdraws);
+            }
         }
     }
 
@@ -486,4 +510,14 @@
             }
         }
     }
+
+    /**
+     * Listener for route events.
+     */
+    private class InternalRouteListener implements RouteListener {
+        @Override
+        public void update(Collection<RouteUpdate> routeUpdates) {
+            Router.this.update(routeUpdates);
+        }
+    }
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpConstants.java
similarity index 99%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpConstants.java
index b009c17..96d74e2 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpConstants.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpConstants.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
 
 /**
  * BGP related constants.
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpFrameDecoder.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpFrameDecoder.java
similarity index 93%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpFrameDecoder.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpFrameDecoder.java
index 4c9b56e..053d0bf 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpFrameDecoder.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpFrameDecoder.java
@@ -13,14 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
 
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
 import org.jboss.netty.channel.Channel;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.jboss.netty.handler.codec.frame.FrameDecoder;
-import org.onosproject.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -86,9 +85,9 @@
                 // ERROR: Connection Not Synchronized
                 //
                 // Send NOTIFICATION and close the connection
-                int errorCode = MessageHeaderError.ERROR_CODE;
+                int errorCode = BgpConstants.Notifications.MessageHeaderError.ERROR_CODE;
                 int errorSubcode =
-                    MessageHeaderError.CONNECTION_NOT_SYNCHRONIZED;
+                    BgpConstants.Notifications.MessageHeaderError.CONNECTION_NOT_SYNCHRONIZED;
                 ChannelBuffer txMessage =
                     BgpNotification.prepareBgpNotification(errorCode,
                                                            errorSubcode,
@@ -162,8 +161,8 @@
             // ERROR: Bad Message Type
             //
             // Send NOTIFICATION and close the connection
-            int errorCode = MessageHeaderError.ERROR_CODE;
-            int errorSubcode = MessageHeaderError.BAD_MESSAGE_TYPE;
+            int errorCode = BgpConstants.Notifications.MessageHeaderError.ERROR_CODE;
+            int errorSubcode = BgpConstants.Notifications.MessageHeaderError.BAD_MESSAGE_TYPE;
             ChannelBuffer data = ChannelBuffers.buffer(1);
             data.writeByte(type);
             ChannelBuffer txMessage =
diff --git a/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpInfoService.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpInfoService.java
new file mode 100644
index 0000000..d7914e6
--- /dev/null
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpInfoService.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.routing.bgp;
+
+import java.util.Collection;
+
+/**
+ * Provides information about BGP peering and routes.
+ */
+public interface BgpInfoService {
+
+    /**
+     * Gets the BGP sessions.
+     *
+     * @return the BGP sessions
+     */
+    public Collection<BgpSession> getBgpSessions();
+
+    /**
+     * Gets the selected IPv4 BGP routes among all BGP sessions.
+     *
+     * @return the selected IPv4 BGP routes among all BGP sessions
+     */
+    public Collection<BgpRouteEntry> getBgpRoutes4();
+
+    /**
+     * Gets the selected IPv6 BGP routes among all BGP sessions.
+     *
+     * @return the selected IPv6 BGP routes among all BGP sessions
+     */
+    public Collection<BgpRouteEntry> getBgpRoutes6();
+}
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpKeepalive.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpKeepalive.java
similarity index 98%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpKeepalive.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpKeepalive.java
index fe9404c..186dfeda 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpKeepalive.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpKeepalive.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
 
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpMessage.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpMessage.java
similarity index 98%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpMessage.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpMessage.java
index 5c7053c..63a5dbd 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpMessage.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpMessage.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
 
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpNotification.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpNotification.java
similarity index 94%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpNotification.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpNotification.java
index cf6bbb7..16ec951 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpNotification.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpNotification.java
@@ -13,12 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
 
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
 import org.jboss.netty.channel.ChannelHandlerContext;
-import org.onosproject.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -117,8 +116,8 @@
      * @return the message to transmit (BGP header included)
      */
     static ChannelBuffer prepareBgpNotificationBadMessageLength(int length) {
-        int errorCode = MessageHeaderError.ERROR_CODE;
-        int errorSubcode = MessageHeaderError.BAD_MESSAGE_LENGTH;
+        int errorCode = BgpConstants.Notifications.MessageHeaderError.ERROR_CODE;
+        int errorSubcode = BgpConstants.Notifications.MessageHeaderError.BAD_MESSAGE_LENGTH;
         ChannelBuffer data = ChannelBuffers.buffer(2);
         data.writeShort(length);
 
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpOpen.java
similarity index 72%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpOpen.java
index 93ef852..3216aec 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpOpen.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpOpen.java
@@ -13,18 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
 
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.onlab.packet.Ip4Address;
-import org.onosproject.sdnip.bgp.BgpConstants.Notifications;
-import org.onosproject.sdnip.bgp.BgpConstants.Notifications.OpenMessageError;
-import org.onosproject.sdnip.bgp.BgpConstants.Open.Capabilities;
-import org.onosproject.sdnip.bgp.BgpConstants.Open.Capabilities.MultiprotocolExtensions;
-import org.onosproject.sdnip.bgp.BgpConstants.Open.Capabilities.As4Octet;
-import org.onosproject.sdnip.bgp.BgpMessage.BgpParseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -86,8 +80,8 @@
             // ERROR: Unsupported Version Number
             //
             // Send NOTIFICATION and close the connection
-            int errorCode = OpenMessageError.ERROR_CODE;
-            int errorSubcode = OpenMessageError.UNSUPPORTED_VERSION_NUMBER;
+            int errorCode = BgpConstants.Notifications.OpenMessageError.ERROR_CODE;
+            int errorSubcode = BgpConstants.Notifications.OpenMessageError.UNSUPPORTED_VERSION_NUMBER;
             ChannelBuffer data = ChannelBuffers.buffer(2);
             data.writeShort(BgpConstants.BGP_VERSION);
             ChannelBuffer txMessage =
@@ -123,8 +117,8 @@
             // ERROR: Unacceptable Hold Time
             //
             // Send NOTIFICATION and close the connection
-            int errorCode = OpenMessageError.ERROR_CODE;
-            int errorSubcode = OpenMessageError.UNACCEPTABLE_HOLD_TIME;
+            int errorCode = BgpConstants.Notifications.OpenMessageError.ERROR_CODE;
+            int errorSubcode = BgpConstants.Notifications.OpenMessageError.UNACCEPTABLE_HOLD_TIME;
             ChannelBuffer txMessage =
                 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
                                                        null);
@@ -149,7 +143,7 @@
         // Parse the Optional Parameters
         try {
             parseOptionalParameters(bgpSession, ctx, message);
-        } catch (BgpParseException e) {
+        } catch (BgpMessage.BgpParseException e) {
             // ERROR: Error parsing optional parameters
             log.debug("BGP RX OPEN Error from {}: " +
                       "Exception parsing Optional Parameters: {}",
@@ -158,8 +152,8 @@
             // ERROR: Invalid Optional Parameters: Unspecific
             //
             // Send NOTIFICATION and close the connection
-            int errorCode = OpenMessageError.ERROR_CODE;
-            int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC;
+            int errorCode = BgpConstants.Notifications.OpenMessageError.ERROR_CODE;
+            int errorSubcode = BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC;
             ChannelBuffer txMessage =
                 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
                                                        null);
@@ -202,8 +196,8 @@
                 // ERROR: Bad Peer AS
                 //
                 // Send NOTIFICATION and close the connection
-                int errorCode = OpenMessageError.ERROR_CODE;
-                int errorSubcode = OpenMessageError.BAD_PEER_AS;
+                int errorCode = BgpConstants.Notifications.OpenMessageError.ERROR_CODE;
+                int errorSubcode = BgpConstants.Notifications.OpenMessageError.BAD_PEER_AS;
                 ChannelBuffer txMessage =
                     BgpNotification.prepareBgpNotification(errorCode,
                                                            errorSubcode, null);
@@ -268,12 +262,12 @@
      * @param bgpSession the BGP Session to use
      * @param ctx the Channel Handler Context
      * @param message the message to process
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static void parseOptionalParameters(BgpSession bgpSession,
                                                 ChannelHandlerContext ctx,
                                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
 
         //
         // Get and verify the Optional Parameters Length
@@ -284,7 +278,7 @@
             String errorMsg = "Invalid Optional Parameter Length field " +
                 optParamLength + ". Remaining Optional Parameters " +
                 message.readableBytes();
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
         if (optParamLength == 0) {
             return;                     // No Optional Parameters
@@ -299,25 +293,25 @@
             if (message.readerIndex() >= optParamEnd) {
                 // ERROR: Malformed Optional Parameters
                 String errorMsg = "Malformed Optional Parameters";
-                throw new BgpParseException(errorMsg);
+                throw new BgpMessage.BgpParseException(errorMsg);
             }
             int paramLen = message.readUnsignedByte();
             if (message.readerIndex() + paramLen > optParamEnd) {
                 // ERROR: Malformed Optional Parameters
                 String errorMsg = "Malformed Optional Parameters";
-                throw new BgpParseException(errorMsg);
+                throw new BgpMessage.BgpParseException(errorMsg);
             }
 
             //
             // Extract the Optional Parameter Value based on the Parameter Type
             //
             switch (paramType) {
-            case Capabilities.TYPE:
+            case BgpConstants.Open.Capabilities.TYPE:
                 // Optional Parameter Type: Capabilities
-                if (paramLen < Capabilities.MIN_LENGTH) {
+                if (paramLen < BgpConstants.Open.Capabilities.MIN_LENGTH) {
                     // ERROR: Malformed Capability
                     String errorMsg = "Malformed Capability Type " + paramType;
-                    throw new BgpParseException(errorMsg);
+                    throw new BgpMessage.BgpParseException(errorMsg);
                 }
                 int capabEnd = message.readerIndex() + paramLen;
                 int capabCode = message.readUnsignedByte();
@@ -325,16 +319,16 @@
                 if (message.readerIndex() + capabLen > capabEnd) {
                     // ERROR: Malformed Capability
                     String errorMsg = "Malformed Capability Type " + paramType;
-                    throw new BgpParseException(errorMsg);
+                    throw new BgpMessage.BgpParseException(errorMsg);
                 }
 
                 switch (capabCode) {
-                case MultiprotocolExtensions.CODE:
+                case BgpConstants.Open.Capabilities.MultiprotocolExtensions.CODE:
                     // Multiprotocol Extensions Capabilities (RFC 4760)
-                    if (capabLen != MultiprotocolExtensions.LENGTH) {
+                    if (capabLen != BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH) {
                         // ERROR: Multiprotocol Extension Length Error
                         String errorMsg = "Multiprotocol Extension Length Error";
-                        throw new BgpParseException(errorMsg);
+                        throw new BgpMessage.BgpParseException(errorMsg);
                     }
                     // Decode the AFI (2 octets) and SAFI (1 octet)
                     int afi = message.readUnsignedShort();
@@ -348,20 +342,20 @@
                     // NOTE: For now we just copy the remote AFI/SAFI setting
                     // to the local configuration.
                     //
-                    if (afi == MultiprotocolExtensions.AFI_IPV4 &&
-                        safi == MultiprotocolExtensions.SAFI_UNICAST) {
+                    if (afi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4 &&
+                        safi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST) {
                         bgpSession.remoteInfo().setIpv4Unicast();
                         bgpSession.localInfo().setIpv4Unicast();
-                    } else if (afi == MultiprotocolExtensions.AFI_IPV4 &&
-                               safi == MultiprotocolExtensions.SAFI_MULTICAST) {
+                    } else if (afi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4 &&
+                               safi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_MULTICAST) {
                         bgpSession.remoteInfo().setIpv4Multicast();
                         bgpSession.localInfo().setIpv4Multicast();
-                    } else if (afi == MultiprotocolExtensions.AFI_IPV6 &&
-                               safi == MultiprotocolExtensions.SAFI_UNICAST) {
+                    } else if (afi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6 &&
+                               safi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST) {
                         bgpSession.remoteInfo().setIpv6Unicast();
                         bgpSession.localInfo().setIpv6Unicast();
-                    } else if (afi == MultiprotocolExtensions.AFI_IPV6 &&
-                               safi == MultiprotocolExtensions.SAFI_MULTICAST) {
+                    } else if (afi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6 &&
+                               safi == BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_MULTICAST) {
                         bgpSession.remoteInfo().setIpv6Multicast();
                         bgpSession.localInfo().setIpv6Multicast();
                     } else {
@@ -370,12 +364,12 @@
                     }
                     break;
 
-                case Capabilities.As4Octet.CODE:
+                case BgpConstants.Open.Capabilities.As4Octet.CODE:
                     // Support for 4-octet AS Number Capabilities (RFC 6793)
-                    if (capabLen != Capabilities.As4Octet.LENGTH) {
+                    if (capabLen != BgpConstants.Open.Capabilities.As4Octet.LENGTH) {
                         // ERROR: 4-octet AS Number Capability Length Error
                         String errorMsg = "4-octet AS Number Capability Length Error";
-                        throw new BgpParseException(errorMsg);
+                        throw new BgpMessage.BgpParseException(errorMsg);
                     }
                     long as4Number = message.readUnsignedInt();
 
@@ -430,56 +424,72 @@
 
         // IPv4 unicast
         if (localInfo.ipv4Unicast()) {
-            message.writeByte(Capabilities.TYPE);               // Param type
-            message.writeByte(Capabilities.MIN_LENGTH +
-                              MultiprotocolExtensions.LENGTH);  // Param len
-            message.writeByte(MultiprotocolExtensions.CODE);    // Capab. code
-            message.writeByte(MultiprotocolExtensions.LENGTH);  // Capab. len
-            message.writeShort(MultiprotocolExtensions.AFI_IPV4);
+            message.writeByte(BgpConstants.Open.Capabilities.TYPE);               // Param type
+            message.writeByte(BgpConstants.Open.Capabilities.MIN_LENGTH +
+                              BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH);  // Param len
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.CODE);    // Capab. code
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH);  // Capab. len
+            message.writeShort(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4);
             message.writeByte(0);               // Reserved field
-            message.writeByte(MultiprotocolExtensions.SAFI_UNICAST);
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST);
         }
         // IPv4 multicast
         if (localInfo.ipv4Multicast()) {
-            message.writeByte(Capabilities.TYPE);               // Param type
-            message.writeByte(Capabilities.MIN_LENGTH +
-                              MultiprotocolExtensions.LENGTH);  // Param len
-            message.writeByte(MultiprotocolExtensions.CODE);    // Capab. code
-            message.writeByte(MultiprotocolExtensions.LENGTH);  // Capab. len
-            message.writeShort(MultiprotocolExtensions.AFI_IPV4);
+            message.writeByte(BgpConstants.Open.Capabilities.TYPE);               // Param type
+            message.writeByte(BgpConstants.Open.Capabilities.MIN_LENGTH +
+                              BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH);  // Param len
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.CODE);    // Capab. code
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH);  // Capab. len
+            message.writeShort(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4);
             message.writeByte(0);               // Reserved field
-            message.writeByte(MultiprotocolExtensions.SAFI_MULTICAST);
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_MULTICAST);
         }
         // IPv6 unicast
         if (localInfo.ipv6Unicast()) {
-            message.writeByte(Capabilities.TYPE);               // Param type
-            message.writeByte(Capabilities.MIN_LENGTH +
-                              MultiprotocolExtensions.LENGTH);  // Param len
-            message.writeByte(MultiprotocolExtensions.CODE);    // Capab. code
-            message.writeByte(MultiprotocolExtensions.LENGTH);  // Capab. len
-            message.writeShort(MultiprotocolExtensions.AFI_IPV6);
+            message.writeByte(BgpConstants.Open.Capabilities.TYPE);               // Param type
+            message.writeByte(BgpConstants.Open.Capabilities.MIN_LENGTH +
+                              BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH);  // Param len
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.CODE);    // Capab. code
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH);  // Capab. len
+            message.writeShort(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6);
             message.writeByte(0);               // Reserved field
-            message.writeByte(MultiprotocolExtensions.SAFI_UNICAST);
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST);
         }
         // IPv6 multicast
         if (localInfo.ipv6Multicast()) {
-            message.writeByte(Capabilities.TYPE);               // Param type
-            message.writeByte(Capabilities.MIN_LENGTH +
-                              MultiprotocolExtensions.LENGTH);  // Param len
-            message.writeByte(MultiprotocolExtensions.CODE);    // Capab. code
-            message.writeByte(MultiprotocolExtensions.LENGTH);  // Capab. len
-            message.writeShort(MultiprotocolExtensions.AFI_IPV6);
+            message.writeByte(BgpConstants.Open.Capabilities.TYPE);               // Param type
+            message.writeByte(BgpConstants.Open.Capabilities.MIN_LENGTH +
+                              BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH);  // Param len
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.CODE);    // Capab. code
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.LENGTH);  // Capab. len
+            message.writeShort(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6);
             message.writeByte(0);               // Reserved field
-            message.writeByte(MultiprotocolExtensions.SAFI_MULTICAST);
+            message.writeByte(
+                    BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_MULTICAST);
         }
 
         // 4 octet AS path capability
         if (localInfo.as4OctetCapability()) {
-            message.writeByte(Capabilities.TYPE);               // Param type
-            message.writeByte(Capabilities.MIN_LENGTH +
-                              As4Octet.LENGTH);                 // Param len
-            message.writeByte(As4Octet.CODE);                   // Capab. code
-            message.writeByte(As4Octet.LENGTH);                 // Capab. len
+            message.writeByte(BgpConstants.Open.Capabilities.TYPE);               // Param type
+            message.writeByte(BgpConstants.Open.Capabilities.MIN_LENGTH +
+                              BgpConstants.Open.Capabilities.As4Octet.LENGTH);                 // Param len
+            message.writeByte(BgpConstants.Open.Capabilities.As4Octet.CODE);                   // Capab. code
+            message.writeByte(BgpConstants.Open.Capabilities.As4Octet.LENGTH);                 // Capab. len
             message.writeInt((int) localInfo.as4Number());
         }
         return message;
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteEntry.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpRouteEntry.java
similarity index 93%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteEntry.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpRouteEntry.java
index 3abdc79..e7e7f3c 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteEntry.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpRouteEntry.java
@@ -13,20 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.routingapi.RouteEntry;
 
 import java.util.ArrayList;
 import java.util.Objects;
 
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.Ip4Address;
-import org.onosproject.sdnip.RouteEntry;
-import org.onosproject.sdnip.bgp.BgpConstants.Update;
-
-import com.google.common.base.MoreObjects;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * Represents a route in BGP.
@@ -37,7 +35,7 @@
     private final byte origin;          // Route ORIGIN: IGP, EGP, INCOMPLETE
     private final AsPath asPath;        // The AS Path
     private final long localPref;       // The local preference for the route
-    private long multiExitDisc = Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
+    private long multiExitDisc = BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
 
     /**
      * Class constructor.
@@ -129,8 +127,8 @@
 
         // Find the first Path Segment by ignoring the AS_CONFED_* segments
         for (PathSegment pathSegment : asPath.getPathSegments()) {
-            if ((pathSegment.getType() == Update.AsPath.AS_SET) ||
-                (pathSegment.getType() == Update.AsPath.AS_SEQUENCE)) {
+            if ((pathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) ||
+                (pathSegment.getType() == BgpConstants.Update.AsPath.AS_SEQUENCE)) {
                 firstPathSegment = pathSegment;
                 break;
             }
@@ -139,7 +137,7 @@
             return true;                // Local route: no path segments
         }
         // If the first path segment is AS_SET, the route is considered local
-        if (firstPathSegment.getType() == Update.AsPath.AS_SET) {
+        if (firstPathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) {
             return true;
         }
 
@@ -164,8 +162,8 @@
 
         // Find the first Path Segment by ignoring the AS_CONFED_* segments
         for (PathSegment pathSegment : asPath.getPathSegments()) {
-            if ((pathSegment.getType() == Update.AsPath.AS_SET) ||
-                (pathSegment.getType() == Update.AsPath.AS_SEQUENCE)) {
+            if ((pathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) ||
+                (pathSegment.getType() == BgpConstants.Update.AsPath.AS_SEQUENCE)) {
                 firstPathSegment = pathSegment;
                 break;
             }
@@ -340,7 +338,7 @@
         @Override
         public String toString() {
             return MoreObjects.toStringHelper(getClass())
-                .add("type", Update.AsPath.typeToString(type))
+                .add("type", BgpConstants.Update.AsPath.typeToString(type))
                 .add("segmentAsNumbers", this.segmentAsNumbers)
                 .toString();
         }
@@ -370,16 +368,16 @@
              int pl = 0;
              for (PathSegment pathSegment : pathSegments) {
                  switch (pathSegment.getType()) {
-                 case Update.AsPath.AS_SET:
+                 case BgpConstants.Update.AsPath.AS_SET:
                      pl++;              // AS_SET counts as 1
                      break;
-                 case Update.AsPath.AS_SEQUENCE:
+                 case BgpConstants.Update.AsPath.AS_SEQUENCE:
                      // Count each AS number
                      pl += pathSegment.getSegmentAsNumbers().size();
                      break;
-                 case Update.AsPath.AS_CONFED_SEQUENCE:
+                 case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
                      break;             // Ignore
-                 case Update.AsPath.AS_CONFED_SET:
+                 case BgpConstants.Update.AsPath.AS_CONFED_SET:
                      break;             // Ignore
                  default:
                      // NOTE: What to do if the Path Segment type is unknown?
@@ -487,7 +485,7 @@
             .add("prefix", prefix())
             .add("nextHop", nextHop())
             .add("bgpId", bgpSession.remoteInfo().bgpId())
-            .add("origin", Update.Origin.typeToString(origin))
+            .add("origin", BgpConstants.Update.Origin.typeToString(origin))
             .add("asPath", asPath)
             .add("localPref", localPref)
             .add("multiExitDisc", multiExitDisc)
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteSelector.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpRouteSelector.java
similarity index 98%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteSelector.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpRouteSelector.java
index 1569c07..e95aaff 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpRouteSelector.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpRouteSelector.java
@@ -13,16 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.routingapi.RouteUpdate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.Collection;
 import java.util.LinkedList;
 
-import org.onlab.packet.IpPrefix;
-import org.onosproject.sdnip.RouteUpdate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 /**
  * Class to receive and process the BGP routes from each BGP Session/Peer.
  */
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSession.java
similarity index 97%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSession.java
index ace6b53..e0a2617 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSession.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSession.java
@@ -13,15 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
-
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.TimeUnit;
+package org.onosproject.routing.bgp;
 
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -32,15 +24,21 @@
 import org.jboss.netty.util.Timeout;
 import org.jboss.netty.util.Timer;
 import org.jboss.netty.util.TimerTask;
-import org.onlab.packet.IpPrefix;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Prefix;
-import org.onosproject.sdnip.bgp.BgpConstants.Notifications;
-import org.onosproject.sdnip.bgp.BgpConstants.Notifications.HoldTimerExpired;
+import org.onlab.packet.IpPrefix;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Class for handling the BGP peer sessions.
  * There is one instance per each BGP peer session.
@@ -463,8 +461,8 @@
             // ERROR: Invalid Optional Parameter Length field: Unspecific
             //
             // Send NOTIFICATION and close the connection
-            int errorCode = HoldTimerExpired.ERROR_CODE;
-            int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC;
+            int errorCode = BgpConstants.Notifications.HoldTimerExpired.ERROR_CODE;
+            int errorSubcode = BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC;
             ChannelBuffer txMessage =
                 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
                                                        null);
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSessionInfo.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSessionInfo.java
similarity index 99%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSessionInfo.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSessionInfo.java
index a492d80..a127455 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSessionInfo.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSessionInfo.java
@@ -13,10 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
+
+import org.onlab.packet.Ip4Address;
 
 import java.net.SocketAddress;
-import org.onlab.packet.Ip4Address;
 
 /**
  * Class for keeping information about a BGP session.
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSessionManager.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSessionManager.java
similarity index 93%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSessionManager.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSessionManager.java
index 2ca2972..cfd0081 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpSessionManager.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpSessionManager.java
@@ -13,8 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
 
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
 import org.jboss.netty.bootstrap.ServerBootstrap;
 import org.jboss.netty.channel.Channel;
 import org.jboss.netty.channel.ChannelException;
@@ -29,7 +31,8 @@
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Prefix;
 import org.onlab.packet.IpPrefix;
-import org.onosproject.sdnip.RouteListener;
+import org.onosproject.routingapi.BgpService;
+import org.onosproject.routingapi.RouteListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -47,7 +50,9 @@
 /**
  * BGP Session Manager class.
  */
-public class BgpSessionManager {
+@Component(immediate = true)
+@Service
+public class BgpSessionManager implements BgpInfoService, BgpService {
     private static final Logger log =
             LoggerFactory.getLogger(BgpSessionManager.class);
 
@@ -65,16 +70,7 @@
     private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRoutes6 =
             new ConcurrentHashMap<>();
 
-    private final RouteListener routeListener;
-
-    /**
-     * Constructor for given route listener.
-     *
-     * @param routeListener the route listener to use
-     */
-    public BgpSessionManager(RouteListener routeListener) {
-        this.routeListener = checkNotNull(routeListener);
-    }
+    private RouteListener routeListener;
 
     /**
      * Checks whether the BGP Session Manager is shutdown.
@@ -248,16 +244,13 @@
         return bgpRouteSelector;
     }
 
-    /**
-     * Starts up BGP Session Manager operation.
-     *
-     * @param listenPortNumber the port number to listen on. By default
-     *                         it should be BgpConstants.BGP_PORT (179)
-     */
-    public void start(int listenPortNumber) {
+    @Override
+    public void start(RouteListener routeListener, int listenPortNumber) {
         log.debug("BGP Session Manager start.");
         isShutdown = false;
 
+        this.routeListener = checkNotNull(routeListener);
+
         ChannelFactory channelFactory = new NioServerSocketChannelFactory(
                 newCachedThreadPool(namedThreads("onos-bgp-sm-boss-%d")),
                 newCachedThreadPool(namedThreads("onos-bgp-sm-worker-%d")));
@@ -294,9 +287,7 @@
         }
     }
 
-    /**
-     * Stops the BGP Session Manager operation.
-     */
+    @Override
     public void stop() {
         isShutdown = true;
         allChannels.close().awaitUninterruptibly();
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpUpdate.java
similarity index 87%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/BgpUpdate.java
index 2068324..e9abd9c 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/BgpUpdate.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/BgpUpdate.java
@@ -13,12 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+package org.onosproject.routing.bgp;
 
 import org.apache.commons.lang3.tuple.Pair;
 import org.jboss.netty.buffer.ChannelBuffer;
@@ -28,14 +23,14 @@
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Address;
 import org.onlab.packet.Ip6Prefix;
-import org.onosproject.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError;
-import org.onosproject.sdnip.bgp.BgpConstants.Open.Capabilities.MultiprotocolExtensions;
-import org.onosproject.sdnip.bgp.BgpConstants.Update;
-import org.onosproject.sdnip.bgp.BgpConstants.Update.AsPath;
-import org.onosproject.sdnip.bgp.BgpMessage.BgpParseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * A class for handling BGP UPDATE messages.
  */
@@ -102,7 +97,7 @@
         try {
             withdrawnPrefixes = parsePackedIp4Prefixes(withdrawnRoutesLength,
                                                        message);
-        } catch (BgpParseException e) {
+        } catch (BgpMessage.BgpParseException e) {
             // ERROR: Invalid Network Field
             log.debug("Exception parsing Withdrawn Prefixes from BGP peer {}: ",
                       bgpSession.remoteInfo().bgpId(), e);
@@ -124,7 +119,7 @@
         //
         try {
             parsePathAttributes(bgpSession, ctx, message, decodedBgpRoutes);
-        } catch (BgpParseException e) {
+        } catch (BgpMessage.BgpParseException e) {
             log.debug("Exception parsing Path Attributes from BGP peer {}: ",
                       bgpSession.remoteInfo().bgpId(), e);
             // NOTE: The session was already closed, so nothing else to do
@@ -179,7 +174,7 @@
      * @param decodedBgpRoutes the container to store the decoded BGP Route
      * Entries. It might already contain some route entries such as withdrawn
      * IPv4 prefixes
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     // CHECKSTYLE IGNORE MethodLength FOR NEXT 300 LINES
     private static void parsePathAttributes(
@@ -187,7 +182,7 @@
                                         ChannelHandlerContext ctx,
                                         ChannelBuffer message,
                                         DecodedBgpRoutes decodedBgpRoutes)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
 
         //
         // Parsed values
@@ -195,10 +190,11 @@
         Short origin = -1;                      // Mandatory
         BgpRouteEntry.AsPath asPath = null;     // Mandatory
         // Legacy NLRI (RFC 4271). Mandatory NEXT_HOP if legacy NLRI is used
-        MpNlri legacyNlri = new MpNlri(MultiprotocolExtensions.AFI_IPV4,
-                                       MultiprotocolExtensions.SAFI_UNICAST);
+        MpNlri legacyNlri = new MpNlri(
+                BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4,
+                                       BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST);
         long multiExitDisc =                    // Optional
-            Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
+            BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
         Long localPref = null;                  // Mandatory
         Long aggregatorAsNumber = null;         // Optional: unused
         Ip4Address aggregatorIpAddress = null;  // Optional: unused
@@ -213,7 +209,7 @@
             // ERROR: Malformed Attribute List
             actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
             String errorMsg = "Malformed Attribute List";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
         if (pathAttributeLength == 0) {
             return;
@@ -229,7 +225,7 @@
                 // ERROR: Malformed Attribute List
                 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
                 String errorMsg = "Malformed Attribute List";
-                throw new BgpParseException(errorMsg);
+                throw new BgpMessage.BgpParseException(errorMsg);
             }
             int attrTypeCode = message.readUnsignedByte();
 
@@ -249,7 +245,7 @@
                 // ERROR: Malformed Attribute List
                 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
                 String errorMsg = "Malformed Attribute List";
-                throw new BgpParseException(errorMsg);
+                throw new BgpMessage.BgpParseException(errorMsg);
             }
             if (extendedLengthBit) {
                 attrLen = message.readUnsignedShort();
@@ -260,7 +256,7 @@
                 // ERROR: Malformed Attribute List
                 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
                 String errorMsg = "Malformed Attribute List";
-                throw new BgpParseException(errorMsg);
+                throw new BgpMessage.BgpParseException(errorMsg);
             }
 
             // Verify the Attribute Flags
@@ -272,21 +268,21 @@
             //
             switch (attrTypeCode) {
 
-            case Update.Origin.TYPE:
+            case BgpConstants.Update.Origin.TYPE:
                 // Attribute Type Code ORIGIN
                 origin = parseAttributeTypeOrigin(bgpSession, ctx,
                                                   attrTypeCode, attrLen,
                                                   attrFlags, message);
                 break;
 
-            case Update.AsPath.TYPE:
+            case BgpConstants.Update.AsPath.TYPE:
                 // Attribute Type Code AS_PATH
                 asPath = parseAttributeTypeAsPath(bgpSession, ctx,
                                                   attrTypeCode, attrLen,
                                                   attrFlags, message);
                 break;
 
-            case Update.NextHop.TYPE:
+            case BgpConstants.Update.NextHop.TYPE:
                 // Attribute Type Code NEXT_HOP
                 legacyNlri.nextHop4 =
                     parseAttributeTypeNextHop(bgpSession, ctx,
@@ -294,7 +290,7 @@
                                               attrFlags, message);
                 break;
 
-            case Update.MultiExitDisc.TYPE:
+            case BgpConstants.Update.MultiExitDisc.TYPE:
                 // Attribute Type Code MULTI_EXIT_DISC
                 multiExitDisc =
                     parseAttributeTypeMultiExitDisc(bgpSession, ctx,
@@ -302,7 +298,7 @@
                                                     attrFlags, message);
                 break;
 
-            case Update.LocalPref.TYPE:
+            case BgpConstants.Update.LocalPref.TYPE:
                 // Attribute Type Code LOCAL_PREF
                 localPref =
                     parseAttributeTypeLocalPref(bgpSession, ctx,
@@ -310,7 +306,7 @@
                                                 attrFlags, message);
                 break;
 
-            case Update.AtomicAggregate.TYPE:
+            case BgpConstants.Update.AtomicAggregate.TYPE:
                 // Attribute Type Code ATOMIC_AGGREGATE
                 parseAttributeTypeAtomicAggregate(bgpSession, ctx,
                                                   attrTypeCode, attrLen,
@@ -318,7 +314,7 @@
                 // Nothing to do: this attribute is primarily informational
                 break;
 
-            case Update.Aggregator.TYPE:
+            case BgpConstants.Update.Aggregator.TYPE:
                 // Attribute Type Code AGGREGATOR
                 Pair<Long, Ip4Address> aggregator =
                     parseAttributeTypeAggregator(bgpSession, ctx,
@@ -328,7 +324,7 @@
                 aggregatorIpAddress = aggregator.getRight();
                 break;
 
-            case Update.MpReachNlri.TYPE:
+            case BgpConstants.Update.MpReachNlri.TYPE:
                 // Attribute Type Code MP_REACH_NLRI
                 MpNlri mpNlriReach =
                     parseAttributeTypeMpReachNlri(bgpSession, ctx,
@@ -340,7 +336,7 @@
                 }
                 break;
 
-            case Update.MpUnreachNlri.TYPE:
+            case BgpConstants.Update.MpUnreachNlri.TYPE:
                 // Attribute Type Code MP_UNREACH_NLRI
                 MpNlri mpNlriUnreach =
                     parseAttributeTypeMpUnreachNlri(bgpSession, ctx,
@@ -360,7 +356,7 @@
                         message);
                     String errorMsg = "Unrecognized Well-known Attribute: " +
                         attrTypeCode;
-                    throw new BgpParseException(errorMsg);
+                    throw new BgpMessage.BgpParseException(errorMsg);
                 }
 
                 // Skip the data from the unrecognized attribute
@@ -381,7 +377,7 @@
                 parsePackedIp4Prefixes(nlriLength, message);
             // Store it inside the legacy NLRI wrapper
             legacyNlri.nlri4 = addedPrefixes4;
-        } catch (BgpParseException e) {
+        } catch (BgpMessage.BgpParseException e) {
             // ERROR: Invalid Network Field
             log.debug("Exception parsing NLRI from BGP peer {}: ",
                       bgpSession.remoteInfo().bgpId(), e);
@@ -486,7 +482,7 @@
      * @param legacyNlri the legacy NLRI. Encapsulates the NEXT_HOP well-known
      * mandatory attribute (mandatory if legacy NLRI is used).
      * @param mpNlriReachList the Multiprotocol NLRI attributes
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static void verifyBgpUpdateWellKnownAttributes(
                                 BgpSession bgpSession,
@@ -496,7 +492,7 @@
                                 Long localPref,
                                 MpNlri legacyNlri,
                                 Collection<MpNlri> mpNlriReachList)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
         boolean hasNlri = false;
         boolean hasLegacyNlri = false;
 
@@ -525,32 +521,32 @@
         //
         if (hasNlri && ((origin == null) || (origin == -1))) {
             // Missing Attribute Type Code ORIGIN
-            int type = Update.Origin.TYPE;
+            int type = BgpConstants.Update.Origin.TYPE;
             actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
             String errorMsg = "Missing Well-known Attribute: ORIGIN";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
         if (hasNlri && (asPath == null)) {
             // Missing Attribute Type Code AS_PATH
-            int type = Update.AsPath.TYPE;
+            int type = BgpConstants.Update.AsPath.TYPE;
             actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
             String errorMsg = "Missing Well-known Attribute: AS_PATH";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
         if (hasNlri && (localPref == null)) {
             // Missing Attribute Type Code LOCAL_PREF
             // NOTE: Required for iBGP
-            int type = Update.LocalPref.TYPE;
+            int type = BgpConstants.Update.LocalPref.TYPE;
             actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
             String errorMsg = "Missing Well-known Attribute: LOCAL_PREF";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
         if (hasLegacyNlri && (legacyNlri.nextHop4 == null)) {
             // Missing Attribute Type Code NEXT_HOP
-            int type = Update.NextHop.TYPE;
+            int type = BgpConstants.Update.NextHop.TYPE;
             actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
             String errorMsg = "Missing Well-known Attribute: NEXT_HOP";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
     }
 
@@ -563,7 +559,7 @@
      * @param attrLen the attribute length (in octets)
      * @param attrFlags the attribute flags
      * @param message the message to parse
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static void verifyBgpUpdateAttributeFlags(
                                 BgpSession bgpSession,
@@ -572,7 +568,7 @@
                                 int attrLen,
                                 int attrFlags,
                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
 
         //
         // Assign the Attribute Type Name and the Well-known flag
@@ -580,39 +576,39 @@
         String typeName = "UNKNOWN";
         boolean isWellKnown = false;
         switch (attrTypeCode) {
-        case Update.Origin.TYPE:
+        case BgpConstants.Update.Origin.TYPE:
             isWellKnown = true;
             typeName = "ORIGIN";
             break;
-        case Update.AsPath.TYPE:
+        case BgpConstants.Update.AsPath.TYPE:
             isWellKnown = true;
             typeName = "AS_PATH";
             break;
-        case Update.NextHop.TYPE:
+        case BgpConstants.Update.NextHop.TYPE:
             isWellKnown = true;
             typeName = "NEXT_HOP";
             break;
-        case Update.MultiExitDisc.TYPE:
+        case BgpConstants.Update.MultiExitDisc.TYPE:
             isWellKnown = false;
             typeName = "MULTI_EXIT_DISC";
             break;
-        case Update.LocalPref.TYPE:
+        case BgpConstants.Update.LocalPref.TYPE:
             isWellKnown = true;
             typeName = "LOCAL_PREF";
             break;
-        case Update.AtomicAggregate.TYPE:
+        case BgpConstants.Update.AtomicAggregate.TYPE:
             isWellKnown = true;
             typeName = "ATOMIC_AGGREGATE";
             break;
-        case Update.Aggregator.TYPE:
+        case BgpConstants.Update.Aggregator.TYPE:
             isWellKnown = false;
             typeName = "AGGREGATOR";
             break;
-        case Update.MpReachNlri.TYPE:
+        case BgpConstants.Update.MpReachNlri.TYPE:
             isWellKnown = false;
             typeName = "MP_REACH_NLRI";
             break;
-        case Update.MpUnreachNlri.TYPE:
+        case BgpConstants.Update.MpUnreachNlri.TYPE:
             isWellKnown = false;
             typeName = "MP_UNREACH_NLRI";
             break;
@@ -643,7 +639,7 @@
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Attribute Flags Error for " + typeName + ": " +
                 attrFlags;
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
     }
 
@@ -657,7 +653,7 @@
      * @param attrFlags the attribute flags
      * @param message the message to parse
      * @return the parsed ORIGIN value
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static short parseAttributeTypeOrigin(
                                 BgpSession bgpSession,
@@ -666,25 +662,25 @@
                                 int attrLen,
                                 int attrFlags,
                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
 
         // Check the Attribute Length
-        if (attrLen != Update.Origin.LENGTH) {
+        if (attrLen != BgpConstants.Update.Origin.LENGTH) {
             // ERROR: Attribute Length Error
             actionsBgpUpdateAttributeLengthError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Attribute Length Error";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         message.markReaderIndex();
         short origin = message.readUnsignedByte();
         switch (origin) {
-        case Update.Origin.IGP:
+        case BgpConstants.Update.Origin.IGP:
             // FALLTHROUGH
-        case Update.Origin.EGP:
+        case BgpConstants.Update.Origin.EGP:
             // FALLTHROUGH
-        case Update.Origin.INCOMPLETE:
+        case BgpConstants.Update.Origin.INCOMPLETE:
             break;
         default:
             // ERROR: Invalid ORIGIN Attribute
@@ -693,7 +689,7 @@
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message,
                 origin);
             String errorMsg = "Invalid ORIGIN Attribute: " + origin;
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         return origin;
@@ -709,7 +705,7 @@
      * @param attrFlags the attribute flags
      * @param message the message to parse
      * @return the parsed AS Path
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static BgpRouteEntry.AsPath parseAttributeTypeAsPath(
                                                 BgpSession bgpSession,
@@ -718,7 +714,7 @@
                                                 int attrLen,
                                                 int attrFlags,
                                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
         ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
 
         //
@@ -729,7 +725,7 @@
                 // ERROR: Malformed AS_PATH
                 actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
                 String errorMsg = "Malformed AS Path";
-                throw new BgpParseException(errorMsg);
+                throw new BgpMessage.BgpParseException(errorMsg);
             }
             // Get the Path Segment Type and Length (in number of ASes)
             short pathSegmentType = message.readUnsignedByte();
@@ -738,13 +734,13 @@
 
             // Verify the Path Segment Type
             switch (pathSegmentType) {
-            case Update.AsPath.AS_SET:
+            case BgpConstants.Update.AsPath.AS_SET:
                 // FALLTHROUGH
-            case Update.AsPath.AS_SEQUENCE:
+            case BgpConstants.Update.AsPath.AS_SEQUENCE:
                 // FALLTHROUGH
-            case Update.AsPath.AS_CONFED_SEQUENCE:
+            case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
                 // FALLTHROUGH
-            case Update.AsPath.AS_CONFED_SET:
+            case BgpConstants.Update.AsPath.AS_CONFED_SET:
                 break;
             default:
                 // ERROR: Invalid Path Segment Type
@@ -756,15 +752,15 @@
                 actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
                 String errorMsg =
                     "Invalid AS Path Segment Type: " + pathSegmentType;
-                throw new BgpParseException(errorMsg);
+                throw new BgpMessage.BgpParseException(errorMsg);
             }
 
             // 4-octet AS number handling.
             int asPathLen;
             if (bgpSession.isAs4OctetCapable()) {
-                asPathLen = AsPath.AS_4OCTET_LENGTH;
+                asPathLen = BgpConstants.Update.AsPath.AS_4OCTET_LENGTH;
             } else {
-                asPathLen = AsPath.AS_LENGTH;
+                asPathLen = BgpConstants.Update.AsPath.AS_LENGTH;
             }
 
             // Parse the AS numbers
@@ -772,13 +768,13 @@
                 // ERROR: Malformed AS_PATH
                 actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
                 String errorMsg = "Malformed AS Path";
-                throw new BgpParseException(errorMsg);
+                throw new BgpMessage.BgpParseException(errorMsg);
             }
             attrLen -= (asPathLen * pathSegmentLength);
             ArrayList<Long> segmentAsNumbers = new ArrayList<>();
             while (pathSegmentLength-- > 0) {
                 long asNumber;
-                if (asPathLen == AsPath.AS_4OCTET_LENGTH) {
+                if (asPathLen == BgpConstants.Update.AsPath.AS_4OCTET_LENGTH) {
                     asNumber = message.readUnsignedInt();
                 } else {
                     asNumber = message.readUnsignedShort();
@@ -805,7 +801,7 @@
      * @param attrFlags the attribute flags
      * @param message the message to parse
      * @return the parsed NEXT_HOP value
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static Ip4Address parseAttributeTypeNextHop(
                                         BgpSession bgpSession,
@@ -814,15 +810,15 @@
                                         int attrLen,
                                         int attrFlags,
                                         ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
 
         // Check the Attribute Length
-        if (attrLen != Update.NextHop.LENGTH) {
+        if (attrLen != BgpConstants.Update.NextHop.LENGTH) {
             // ERROR: Attribute Length Error
             actionsBgpUpdateAttributeLengthError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Attribute Length Error";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         message.markReaderIndex();
@@ -845,7 +841,7 @@
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message,
                 nextHopAddress);
             String errorMsg = "Invalid NEXT_HOP Attribute: " + nextHopAddress;
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         return nextHopAddress;
@@ -861,7 +857,7 @@
      * @param attrFlags the attribute flags
      * @param message the message to parse
      * @return the parsed MULTI_EXIT_DISC value
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static long parseAttributeTypeMultiExitDisc(
                                 BgpSession bgpSession,
@@ -870,15 +866,15 @@
                                 int attrLen,
                                 int attrFlags,
                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
 
         // Check the Attribute Length
-        if (attrLen != Update.MultiExitDisc.LENGTH) {
+        if (attrLen != BgpConstants.Update.MultiExitDisc.LENGTH) {
             // ERROR: Attribute Length Error
             actionsBgpUpdateAttributeLengthError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Attribute Length Error";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         long multiExitDisc = message.readUnsignedInt();
@@ -895,7 +891,7 @@
      * @param attrFlags the attribute flags
      * @param message the message to parse
      * @return the parsed LOCAL_PREF value
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static long parseAttributeTypeLocalPref(
                                 BgpSession bgpSession,
@@ -904,15 +900,15 @@
                                 int attrLen,
                                 int attrFlags,
                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
 
         // Check the Attribute Length
-        if (attrLen != Update.LocalPref.LENGTH) {
+        if (attrLen != BgpConstants.Update.LocalPref.LENGTH) {
             // ERROR: Attribute Length Error
             actionsBgpUpdateAttributeLengthError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Attribute Length Error";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         long localPref = message.readUnsignedInt();
@@ -928,7 +924,7 @@
      * @param attrLen the attribute length (in octets)
      * @param attrFlags the attribute flags
      * @param message the message to parse
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static void parseAttributeTypeAtomicAggregate(
                                 BgpSession bgpSession,
@@ -937,15 +933,15 @@
                                 int attrLen,
                                 int attrFlags,
                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
 
         // Check the Attribute Length
-        if (attrLen != Update.AtomicAggregate.LENGTH) {
+        if (attrLen != BgpConstants.Update.AtomicAggregate.LENGTH) {
             // ERROR: Attribute Length Error
             actionsBgpUpdateAttributeLengthError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Attribute Length Error";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         // Nothing to do: this attribute is primarily informational
@@ -961,7 +957,7 @@
      * @param attrFlags the attribute flags
      * @param message the message to parse
      * @return the parsed AGGREGATOR value: a tuple of <AS-Number, IP-Address>
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static Pair<Long, Ip4Address> parseAttributeTypeAggregator(
                                                 BgpSession bgpSession,
@@ -970,15 +966,15 @@
                                                 int attrLen,
                                                 int attrFlags,
                                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
 
         // Check the Attribute Length
-        if (attrLen != Update.Aggregator.LENGTH) {
+        if (attrLen != BgpConstants.Update.Aggregator.LENGTH) {
             // ERROR: Attribute Length Error
             actionsBgpUpdateAttributeLengthError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Attribute Length Error";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         // The AGGREGATOR AS number
@@ -1003,7 +999,7 @@
      * @param message the message to parse
      * @return the parsed MP_REACH_NLRI information if recognized, otherwise
      * null
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static MpNlri parseAttributeTypeMpReachNlri(
                                                 BgpSession bgpSession,
@@ -1012,16 +1008,16 @@
                                                 int attrLen,
                                                 int attrFlags,
                                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
         int attributeEnd = message.readerIndex() + attrLen;
 
         // Check the Attribute Length
-        if (attrLen < Update.MpReachNlri.MIN_LENGTH) {
+        if (attrLen < BgpConstants.Update.MpReachNlri.MIN_LENGTH) {
             // ERROR: Attribute Length Error
             actionsBgpUpdateAttributeLengthError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Attribute Length Error";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         message.markReaderIndex();
@@ -1033,9 +1029,9 @@
         // Verify the AFI/SAFI, and skip the attribute if not recognized.
         // NOTE: Currently, we support only IPv4/IPv6 UNICAST
         //
-        if (((afi != MultiprotocolExtensions.AFI_IPV4) &&
-             (afi != MultiprotocolExtensions.AFI_IPV6)) ||
-            (safi != MultiprotocolExtensions.SAFI_UNICAST)) {
+        if (((afi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4) &&
+             (afi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6)) ||
+            (safi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST)) {
             // Skip the attribute
             message.resetReaderIndex();
             message.skipBytes(attrLen);
@@ -1047,10 +1043,10 @@
         //
         int expectedNextHopLen = 0;
         switch (afi) {
-        case MultiprotocolExtensions.AFI_IPV4:
+        case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4:
             expectedNextHopLen = Ip4Address.BYTE_LENGTH;
             break;
-        case MultiprotocolExtensions.AFI_IPV6:
+        case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6:
             expectedNextHopLen = Ip6Address.BYTE_LENGTH;
             break;
         default:
@@ -1064,7 +1060,7 @@
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Invalid next-hop network address length. " +
                 "Received " + nextHopLen + " expected " + expectedNextHopLen;
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
         // NOTE: We use "+ 1" to take into account the Reserved field (1 octet)
         if (message.readerIndex() + nextHopLen + 1 >= attributeEnd) {
@@ -1073,7 +1069,7 @@
             actionsBgpUpdateOptionalAttributeError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Malformed next-hop network address";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         //
@@ -1085,7 +1081,7 @@
         MpNlri mpNlri = new MpNlri(afi, safi);
         try {
             switch (afi) {
-            case MultiprotocolExtensions.AFI_IPV4:
+            case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4:
                 // The next-hop address
                 mpNlri.nextHop4 = Ip4Address.valueOf(nextHopBuffer);
                 // The NLRI
@@ -1093,7 +1089,7 @@
                                         attributeEnd - message.readerIndex(),
                                         message);
                 break;
-            case MultiprotocolExtensions.AFI_IPV6:
+            case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6:
                 // The next-hop address
                 mpNlri.nextHop6 = Ip6Address.valueOf(nextHopBuffer);
                 // The NLRI
@@ -1105,13 +1101,13 @@
                 // UNREACHABLE
                 break;
             }
-        } catch (BgpParseException e) {
+        } catch (BgpMessage.BgpParseException e) {
             // ERROR: Optional Attribute Error
             message.resetReaderIndex();
             actionsBgpUpdateOptionalAttributeError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Malformed network layer reachability information";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         return mpNlri;
@@ -1128,7 +1124,7 @@
      * @param message the message to parse
      * @return the parsed MP_UNREACH_NLRI information if recognized, otherwise
      * null
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static MpNlri parseAttributeTypeMpUnreachNlri(
                                                 BgpSession bgpSession,
@@ -1137,16 +1133,16 @@
                                                 int attrLen,
                                                 int attrFlags,
                                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
         int attributeEnd = message.readerIndex() + attrLen;
 
         // Check the Attribute Length
-        if (attrLen < Update.MpUnreachNlri.MIN_LENGTH) {
+        if (attrLen < BgpConstants.Update.MpUnreachNlri.MIN_LENGTH) {
             // ERROR: Attribute Length Error
             actionsBgpUpdateAttributeLengthError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Attribute Length Error";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         message.markReaderIndex();
@@ -1157,9 +1153,9 @@
         // Verify the AFI/SAFI, and skip the attribute if not recognized.
         // NOTE: Currently, we support only IPv4/IPv6 UNICAST
         //
-        if (((afi != MultiprotocolExtensions.AFI_IPV4) &&
-             (afi != MultiprotocolExtensions.AFI_IPV6)) ||
-            (safi != MultiprotocolExtensions.SAFI_UNICAST)) {
+        if (((afi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4) &&
+             (afi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6)) ||
+            (safi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST)) {
             // Skip the attribute
             message.resetReaderIndex();
             message.skipBytes(attrLen);
@@ -1172,13 +1168,13 @@
         MpNlri mpNlri = new MpNlri(afi, safi);
         try {
             switch (afi) {
-            case MultiprotocolExtensions.AFI_IPV4:
+            case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4:
                 // The Withdrawn Routes
                 mpNlri.nlri4 = parsePackedIp4Prefixes(
                                         attributeEnd - message.readerIndex(),
                                         message);
                 break;
-            case MultiprotocolExtensions.AFI_IPV6:
+            case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6:
                 // The Withdrawn Routes
                 mpNlri.nlri6 = parsePackedIp6Prefixes(
                                         attributeEnd - message.readerIndex(),
@@ -1188,13 +1184,13 @@
                 // UNREACHABLE
                 break;
             }
-        } catch (BgpParseException e) {
+        } catch (BgpMessage.BgpParseException e) {
             // ERROR: Optional Attribute Error
             message.resetReaderIndex();
             actionsBgpUpdateOptionalAttributeError(
                 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
             String errorMsg = "Malformed withdrawn routes";
-            throw new BgpParseException(errorMsg);
+            throw new BgpMessage.BgpParseException(errorMsg);
         }
 
         return mpNlri;
@@ -1211,12 +1207,12 @@
      * @param totalLength the total length of the data to parse
      * @param message the message with data to parse
      * @return a collection of parsed IPv4 network prefixes
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static Collection<Ip4Prefix> parsePackedIp4Prefixes(
                                                 int totalLength,
                                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
         Collection<Ip4Prefix> result = new ArrayList<>();
 
         if (totalLength == 0) {
@@ -1231,7 +1227,7 @@
             int prefixBytelen = (prefixBitlen + 7) / 8;     // Round-up
             if (message.readerIndex() + prefixBytelen > dataEnd) {
                 String errorMsg = "Malformed Network Prefixes";
-                throw new BgpParseException(errorMsg);
+                throw new BgpMessage.BgpParseException(errorMsg);
             }
 
             message.readBytes(buffer, 0, prefixBytelen);
@@ -1254,12 +1250,12 @@
      * @param totalLength the total length of the data to parse
      * @param message the message with data to parse
      * @return a collection of parsed IPv6 network prefixes
-     * @throws BgpParseException
+     * @throws BgpMessage.BgpParseException
      */
     private static Collection<Ip6Prefix> parsePackedIp6Prefixes(
                                                 int totalLength,
                                                 ChannelBuffer message)
-        throws BgpParseException {
+        throws BgpMessage.BgpParseException {
         Collection<Ip6Prefix> result = new ArrayList<>();
 
         if (totalLength == 0) {
@@ -1274,7 +1270,7 @@
             int prefixBytelen = (prefixBitlen + 7) / 8;     // Round-up
             if (message.readerIndex() + prefixBytelen > dataEnd) {
                 String errorMsg = "Malformed Network Prefixes";
-                throw new BgpParseException(errorMsg);
+                throw new BgpMessage.BgpParseException(errorMsg);
             }
 
             message.readBytes(buffer, 0, prefixBytelen);
@@ -1303,8 +1299,8 @@
         // ERROR: Invalid Network Field
         //
         // Send NOTIFICATION and close the connection
-        int errorCode = UpdateMessageError.ERROR_CODE;
-        int errorSubcode = UpdateMessageError.INVALID_NETWORK_FIELD;
+        int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
+        int errorSubcode = BgpConstants.Notifications.UpdateMessageError.INVALID_NETWORK_FIELD;
         ChannelBuffer txMessage =
             BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
                                                    null);
@@ -1329,8 +1325,8 @@
         // ERROR: Malformed Attribute List
         //
         // Send NOTIFICATION and close the connection
-        int errorCode = UpdateMessageError.ERROR_CODE;
-        int errorSubcode = UpdateMessageError.MALFORMED_ATTRIBUTE_LIST;
+        int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
+        int errorSubcode = BgpConstants.Notifications.UpdateMessageError.MALFORMED_ATTRIBUTE_LIST;
         ChannelBuffer txMessage =
             BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
                                                    null);
@@ -1358,8 +1354,8 @@
         // ERROR: Missing Well-known Attribute
         //
         // Send NOTIFICATION and close the connection
-        int errorCode = UpdateMessageError.ERROR_CODE;
-        int errorSubcode = UpdateMessageError.MISSING_WELL_KNOWN_ATTRIBUTE;
+        int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
+        int errorSubcode = BgpConstants.Notifications.UpdateMessageError.MISSING_WELL_KNOWN_ATTRIBUTE;
         ChannelBuffer data = ChannelBuffers.buffer(1);
         data.writeByte(missingAttrTypeCode);
         ChannelBuffer txMessage =
@@ -1396,8 +1392,8 @@
         // ERROR: Invalid ORIGIN Attribute
         //
         // Send NOTIFICATION and close the connection
-        int errorCode = UpdateMessageError.ERROR_CODE;
-        int errorSubcode = UpdateMessageError.INVALID_ORIGIN_ATTRIBUTE;
+        int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
+        int errorSubcode = BgpConstants.Notifications.UpdateMessageError.INVALID_ORIGIN_ATTRIBUTE;
         ChannelBuffer data =
             prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
                                                     attrFlags, message);
@@ -1433,8 +1429,8 @@
         // ERROR: Attribute Flags Error
         //
         // Send NOTIFICATION and close the connection
-        int errorCode = UpdateMessageError.ERROR_CODE;
-        int errorSubcode = UpdateMessageError.ATTRIBUTE_FLAGS_ERROR;
+        int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
+        int errorSubcode = BgpConstants.Notifications.UpdateMessageError.ATTRIBUTE_FLAGS_ERROR;
         ChannelBuffer data =
             prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
                                                     attrFlags, message);
@@ -1473,8 +1469,8 @@
         // ERROR: Invalid NEXT_HOP Attribute
         //
         // Send NOTIFICATION and close the connection
-        int errorCode = UpdateMessageError.ERROR_CODE;
-        int errorSubcode = UpdateMessageError.INVALID_NEXT_HOP_ATTRIBUTE;
+        int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
+        int errorSubcode = BgpConstants.Notifications.UpdateMessageError.INVALID_NEXT_HOP_ATTRIBUTE;
         ChannelBuffer data =
             prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
                                                     attrFlags, message);
@@ -1512,9 +1508,9 @@
         // ERROR: Unrecognized Well-known Attribute
         //
         // Send NOTIFICATION and close the connection
-        int errorCode = UpdateMessageError.ERROR_CODE;
+        int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
         int errorSubcode =
-            UpdateMessageError.UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE;
+            BgpConstants.Notifications.UpdateMessageError.UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE;
         ChannelBuffer data =
             prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
                                                     attrFlags, message);
@@ -1551,9 +1547,9 @@
         // ERROR: Optional Attribute Error
         //
         // Send NOTIFICATION and close the connection
-        int errorCode = UpdateMessageError.ERROR_CODE;
+        int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
         int errorSubcode =
-            UpdateMessageError.OPTIONAL_ATTRIBUTE_ERROR;
+            BgpConstants.Notifications.UpdateMessageError.OPTIONAL_ATTRIBUTE_ERROR;
         ChannelBuffer data =
             prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
                                                     attrFlags, message);
@@ -1589,8 +1585,8 @@
         // ERROR: Attribute Length Error
         //
         // Send NOTIFICATION and close the connection
-        int errorCode = UpdateMessageError.ERROR_CODE;
-        int errorSubcode = UpdateMessageError.ATTRIBUTE_LENGTH_ERROR;
+        int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
+        int errorSubcode = BgpConstants.Notifications.UpdateMessageError.ATTRIBUTE_LENGTH_ERROR;
         ChannelBuffer data =
             prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
                                                     attrFlags, message);
@@ -1618,8 +1614,8 @@
         // ERROR: Malformed AS_PATH
         //
         // Send NOTIFICATION and close the connection
-        int errorCode = UpdateMessageError.ERROR_CODE;
-        int errorSubcode = UpdateMessageError.MALFORMED_AS_PATH;
+        int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
+        int errorSubcode = BgpConstants.Notifications.UpdateMessageError.MALFORMED_AS_PATH;
         ChannelBuffer txMessage =
             BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
                                                    null);
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/package-info.java b/apps/routing/src/main/java/org/onosproject/routing/bgp/package-info.java
similarity index 94%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/package-info.java
rename to apps/routing/src/main/java/org/onosproject/routing/bgp/package-info.java
index 9e1b5d2..e4259b6 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/bgp/package-info.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/bgp/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Implementation of the BGP protocol.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java b/apps/routing/src/main/java/org/onosproject/routing/cli/BgpNeighborsListCommand.java
similarity index 96%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java
rename to apps/routing/src/main/java/org/onosproject/routing/cli/BgpNeighborsListCommand.java
index fe2a4a4..b3cb6d0 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpNeighborsListCommand.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/cli/BgpNeighborsListCommand.java
@@ -13,9 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.cli;
-
-import java.util.Collection;
+package org.onosproject.routing.cli;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -24,8 +22,10 @@
 import org.apache.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.Option;
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.sdnip.SdnIpService;
-import org.onosproject.sdnip.bgp.BgpSession;
+import org.onosproject.routing.bgp.BgpInfoService;
+import org.onosproject.routing.bgp.BgpSession;
+
+import java.util.Collection;
 
 /**
  * Command to show the BGP neighbors.
@@ -53,7 +53,7 @@
 
     @Override
     protected void execute() {
-        SdnIpService service = get(SdnIpService.class);
+        BgpInfoService service = AbstractShellCommand.get(BgpInfoService.class);
         Collection<BgpSession> bgpSessions = service.getBgpSessions();
 
         if (bgpNeighbor != null) {
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpRoutesListCommand.java b/apps/routing/src/main/java/org/onosproject/routing/cli/BgpRoutesListCommand.java
similarity index 96%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpRoutesListCommand.java
rename to apps/routing/src/main/java/org/onosproject/routing/cli/BgpRoutesListCommand.java
index a3b19df..3b032c0 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/BgpRoutesListCommand.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/cli/BgpRoutesListCommand.java
@@ -13,10 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.cli;
-
-import java.util.ArrayList;
-import java.util.Collection;
+package org.onosproject.routing.cli;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -25,10 +22,13 @@
 import org.apache.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.Option;
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.sdnip.SdnIpService;
-import org.onosproject.sdnip.bgp.BgpConstants.Update;
-import org.onosproject.sdnip.bgp.BgpRouteEntry;
-import org.onosproject.sdnip.bgp.BgpSession;
+import org.onosproject.routing.bgp.BgpConstants.Update;
+import org.onosproject.routing.bgp.BgpInfoService;
+import org.onosproject.routing.bgp.BgpRouteEntry;
+import org.onosproject.routing.bgp.BgpSession;
+
+import java.util.ArrayList;
+import java.util.Collection;
 
 /**
  * Command to show the routes learned through BGP.
@@ -59,7 +59,7 @@
 
     @Override
     protected void execute() {
-        SdnIpService service = get(SdnIpService.class);
+        BgpInfoService service = AbstractShellCommand.get(BgpInfoService.class);
 
         // Print summary of the routes
         if (routesSummary) {
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RoutesListCommand.java b/apps/routing/src/main/java/org/onosproject/routing/cli/RoutesListCommand.java
similarity index 95%
rename from apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RoutesListCommand.java
rename to apps/routing/src/main/java/org/onosproject/routing/cli/RoutesListCommand.java
index cebbc43..6c9d688 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/RoutesListCommand.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/cli/RoutesListCommand.java
@@ -13,9 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.cli;
-
-import java.util.Collection;
+package org.onosproject.routing.cli;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -24,8 +22,10 @@
 import org.apache.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.Option;
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.sdnip.RouteEntry;
-import org.onosproject.sdnip.SdnIpService;
+import org.onosproject.routingapi.RouteEntry;
+import org.onosproject.routingapi.RoutingService;
+
+import java.util.Collection;
 
 /**
  * Command to show the list of routes in SDN-IP's routing table.
@@ -49,7 +49,7 @@
 
     @Override
     protected void execute() {
-        SdnIpService service = get(SdnIpService.class);
+        RoutingService service = AbstractShellCommand.get(RoutingService.class);
 
         // Print summary of the routes
         if (routesSummary) {
diff --git a/apps/routing/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/routing/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..5ef971d
--- /dev/null
+++ b/apps/routing/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,29 @@
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+  <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+    <command>
+      <action class="org.onosproject.routing.cli.BgpNeighborsListCommand"/>
+    </command>
+    <command>
+      <action class="org.onosproject.routing.cli.BgpRoutesListCommand"/>
+    </command>
+    <command>
+      <action class="org.onosproject.routing.cli.RoutesListCommand"/>
+    </command>
+  </command-bundle>
+</blueprint>
diff --git a/apps/routing/src/test/java/org/onosproject/routing/RouterAsyncArpTest.java b/apps/routing/src/test/java/org/onosproject/routing/RouterAsyncArpTest.java
new file mode 100644
index 0000000..d9a5ce9
--- /dev/null
+++ b/apps/routing/src/test/java/org/onosproject/routing/RouterAsyncArpTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.routing;
+
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultHost;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.routing.Router.InternalHostListener;
+import org.onosproject.routingapi.BgpService;
+import org.onosproject.routingapi.FibEntry;
+import org.onosproject.routingapi.FibListener;
+import org.onosproject.routingapi.FibUpdate;
+import org.onosproject.routingapi.RouteEntry;
+import org.onosproject.routingapi.RouteListener;
+import org.onosproject.routingapi.RouteUpdate;
+
+import java.util.Collections;
+
+import static org.easymock.EasyMock.*;
+
+/**
+* This class tests adding a route and updating a route.
+* The HostService module answers the MAC address asynchronously.
+*/
+public class RouterAsyncArpTest {
+
+    private HostService hostService;
+    private FibListener fibListener;
+
+    private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000001"),
+            PortNumber.portNumber(1));
+
+    private static final ConnectPoint SW2_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000002"),
+            PortNumber.portNumber(1));
+
+    private static final ConnectPoint SW3_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000003"),
+            PortNumber.portNumber(1));
+
+    private Router router;
+    private InternalHostListener internalHostListener;
+
+    @Before
+    public void setUp() throws Exception {
+        hostService = createMock(HostService.class);
+
+        BgpService bgpService = createMock(BgpService.class);
+        bgpService.start(anyObject(RouteListener.class), anyInt());
+        bgpService.stop();
+        replay(bgpService);
+
+        fibListener = createMock(FibListener.class);
+
+        router = new Router();
+        router.hostService = hostService;
+        router.bgpService = bgpService;
+        router.activate();
+
+        router.start(fibListener);
+
+        internalHostListener = router.new InternalHostListener();
+    }
+
+    @After
+    public void tearDown() {
+        // Called during shutdown
+        reset(hostService);
+        hostService.removeListener(anyObject(HostListener.class));
+
+        router.stop();
+    }
+
+    /**
+     * Tests adding a route entry with asynchronous HostService replies.
+     */
+    @Test
+    public void testRouteAdd() {
+        // Construct a route entry
+        IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+        IpAddress nextHopIp = Ip4Address.valueOf("192.168.10.1");
+
+        RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp);
+
+        // Host service will reply with no hosts when asked
+        reset(hostService);
+        expect(hostService.getHostsByIp(anyObject(IpAddress.class))).andReturn(
+                Collections.emptySet()).anyTimes();
+        hostService.startMonitoringIp(IpAddress.valueOf("192.168.10.1"));
+        replay(hostService);
+
+
+        // Initially when we add the route, no FIB update will be sent
+        replay(fibListener);
+
+        router.processRouteUpdates(Collections.singletonList(
+                new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry)));
+
+        verify(fibListener);
+
+
+        // Now when we send the event, we expect the FIB update to be sent
+        reset(fibListener);
+        FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
+                                         MacAddress.valueOf("00:00:00:00:00:01"));
+
+        fibListener.update(Collections.singletonList(new FibUpdate(
+                FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
+        replay(fibListener);
+
+        Host host = new DefaultHost(ProviderId.NONE, HostId.NONE,
+                                    MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE,
+                                    new HostLocation(
+                                            SW1_ETH1.deviceId(),
+                                            SW1_ETH1.port(), 1),
+                                    Sets.newHashSet(IpAddress.valueOf("192.168.10.1")));
+
+        // Send in the host event
+        internalHostListener.event(
+                new HostEvent(HostEvent.Type.HOST_ADDED, host));
+
+        verify(fibListener);
+    }
+
+    /**
+     * Tests updating a route entry with asynchronous HostService replies.
+     */
+    @Test
+    public void testRouteUpdate() {
+        // Add a route
+        testRouteAdd();
+
+        // Construct a route entry
+        IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+        IpAddress nextHopIp = Ip4Address.valueOf("192.168.20.1");
+
+        RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp);
+
+        // Host service will reply with no hosts when asked
+        reset(hostService);
+        expect(hostService.getHostsByIp(anyObject(IpAddress.class))).andReturn(
+                Collections.emptySet()).anyTimes();
+        hostService.startMonitoringIp(IpAddress.valueOf("192.168.20.1"));
+        replay(hostService);
+
+
+        // Initially when we add the route, the DELETE FIB update will be sent
+        // but the UPDATE FIB update will come later when the MAC is resolved
+        reset(fibListener);
+
+        fibListener.update(Collections.emptyList(), Collections.singletonList(new FibUpdate(
+                FibUpdate.Type.DELETE, new FibEntry(prefix, null, null))));
+        replay(fibListener);
+
+        router.processRouteUpdates(Collections.singletonList(
+                new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry)));
+
+        verify(fibListener);
+
+
+        // Now when we send the event, we expect the FIB update to be sent
+        reset(fibListener);
+        FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
+                                         MacAddress.valueOf("00:00:00:00:00:02"));
+
+        fibListener.update(Collections.singletonList(new FibUpdate(
+                FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
+        replay(fibListener);
+
+        Host host = new DefaultHost(ProviderId.NONE, HostId.NONE,
+                                    MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
+                                    new HostLocation(
+                                            SW1_ETH1.deviceId(),
+                                            SW1_ETH1.port(), 1),
+                                    Sets.newHashSet(IpAddress.valueOf("192.168.20.1")));
+
+        // Send in the host event
+        internalHostListener.event(
+                new HostEvent(HostEvent.Type.HOST_ADDED, host));
+
+        verify(fibListener);
+    }
+}
diff --git a/apps/routing/src/test/java/org/onosproject/routing/RouterTest.java b/apps/routing/src/test/java/org/onosproject/routing/RouterTest.java
new file mode 100644
index 0000000..176c159
--- /dev/null
+++ b/apps/routing/src/test/java/org/onosproject/routing/RouterTest.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.routing;
+
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultHost;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.routingapi.BgpService;
+import org.onosproject.routingapi.FibEntry;
+import org.onosproject.routingapi.FibListener;
+import org.onosproject.routingapi.FibUpdate;
+import org.onosproject.routingapi.RouteEntry;
+import org.onosproject.routingapi.RouteListener;
+import org.onosproject.routingapi.RouteUpdate;
+
+import java.util.Collections;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * This class tests adding a route, updating a route, deleting a route,
+ * and adding a route whose next hop is the local BGP speaker.
+ * <p/>
+ * The HostService answers requests synchronously.
+ */
+public class RouterTest {
+
+    private HostService hostService;
+
+    private FibListener fibListener;
+
+    private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000001"),
+            PortNumber.portNumber(1));
+
+    private static final ConnectPoint SW2_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000002"),
+            PortNumber.portNumber(1));
+
+    private static final ConnectPoint SW3_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000003"),
+            PortNumber.portNumber(1));
+
+    private static final ConnectPoint SW4_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000004"),
+            PortNumber.portNumber(1));
+
+    private Router router;
+
+    @Before
+    public void setUp() throws Exception {
+        setUpHostService();
+
+        BgpService bgpService = createMock(BgpService.class);
+        bgpService.start(anyObject(RouteListener.class), anyInt());
+        bgpService.stop();
+        replay(bgpService);
+
+        fibListener = createMock(FibListener.class);
+
+        router = new Router();
+        router.hostService = hostService;
+        router.bgpService = bgpService;
+        router.activate();
+
+        router.start(fibListener);
+    }
+
+    @After
+    public void tearDown() {
+        router.stop();
+    }
+
+    /**
+     * Sets up the host service with details of some hosts.
+     */
+    private void setUpHostService() {
+        hostService = createMock(HostService.class);
+
+        hostService.addListener(anyObject(HostListener.class));
+        expectLastCall().anyTimes();
+
+        IpAddress host1Address = IpAddress.valueOf("192.168.10.1");
+        Host host1 = new DefaultHost(ProviderId.NONE, HostId.NONE,
+                MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE,
+                new HostLocation(SW1_ETH1, 1),
+                Sets.newHashSet(host1Address));
+
+        expect(hostService.getHostsByIp(host1Address))
+                .andReturn(Sets.newHashSet(host1)).anyTimes();
+        hostService.startMonitoringIp(host1Address);
+        expectLastCall().anyTimes();
+
+
+        IpAddress host2Address = IpAddress.valueOf("192.168.20.1");
+        Host host2 = new DefaultHost(ProviderId.NONE, HostId.NONE,
+                MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
+                new HostLocation(SW2_ETH1, 1),
+                Sets.newHashSet(host2Address));
+
+        expect(hostService.getHostsByIp(host2Address))
+                .andReturn(Sets.newHashSet(host2)).anyTimes();
+        hostService.startMonitoringIp(host2Address);
+        expectLastCall().anyTimes();
+
+        // Next hop on a VLAN
+        IpAddress host3Address = IpAddress.valueOf("192.168.40.1");
+        Host host3 = new DefaultHost(ProviderId.NONE, HostId.NONE,
+                MacAddress.valueOf("00:00:00:00:00:03"), VlanId.vlanId((short) 1),
+                new HostLocation(SW4_ETH1, 1),
+                Sets.newHashSet(host3Address));
+
+        expect(hostService.getHostsByIp(host3Address))
+                .andReturn(Sets.newHashSet(host3)).anyTimes();
+        hostService.startMonitoringIp(host3Address);
+        expectLastCall().anyTimes();
+
+        // Called during shutdown
+        hostService.removeListener(anyObject(HostListener.class));
+
+        replay(hostService);
+    }
+
+    /**
+     * Tests adding a route entry.
+     */
+    @Test
+    public void testRouteAdd() {
+        // Construct a route entry
+        IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+        IpAddress nextHopIp = Ip4Address.valueOf("192.168.10.1");
+
+        RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp);
+
+        // Expected FIB entry
+        FibEntry fibEntry = new FibEntry(prefix, nextHopIp,
+                                         MacAddress.valueOf("00:00:00:00:00:01"));
+
+        fibListener.update(Collections.singletonList(new FibUpdate(
+                FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList());
+
+        replay(fibListener);
+
+        router.processRouteUpdates(Collections.singletonList(
+                new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry)));
+
+        verify(fibListener);
+    }
+
+    /**
+     * Tests updating a route entry.
+     */
+    @Test
+    public void testRouteUpdate() {
+        // Firstly add a route
+        testRouteAdd();
+
+        // Route entry with updated next hop for the original prefix
+        RouteEntry routeEntryUpdate = new RouteEntry(
+                Ip4Prefix.valueOf("1.1.1.0/24"),
+                Ip4Address.valueOf("192.168.20.1"));
+
+        // The old FIB entry will be withdrawn
+        FibEntry withdrawFibEntry = new FibEntry(
+                Ip4Prefix.valueOf("1.1.1.0/24"), null, null);
+
+        // A new FIB entry will be added
+        FibEntry updateFibEntry = new FibEntry(
+                Ip4Prefix.valueOf("1.1.1.0/24"),
+                Ip4Address.valueOf("192.168.20.1"),
+                MacAddress.valueOf("00:00:00:00:00:02"));
+
+        reset(fibListener);
+        fibListener.update(Collections.singletonList(new FibUpdate(
+                                    FibUpdate.Type.UPDATE, updateFibEntry)),
+                           Collections.singletonList(new FibUpdate(
+                                    FibUpdate.Type.DELETE, withdrawFibEntry)));
+
+        replay(fibListener);
+
+        router.processRouteUpdates(Collections.singletonList(new RouteUpdate(
+                RouteUpdate.Type.UPDATE, routeEntryUpdate)));
+
+        verify(fibListener);
+    }
+
+    /**
+     * Tests deleting a route entry.
+     */
+    @Test
+    public void testRouteDelete() {
+        // Firstly add a route
+        testRouteAdd();
+
+        RouteEntry deleteRouteEntry = new RouteEntry(
+                Ip4Prefix.valueOf("1.1.1.0/24"),
+                Ip4Address.valueOf("192.168.10.1"));
+
+        FibEntry deleteFibEntry = new FibEntry(
+                Ip4Prefix.valueOf("1.1.1.0/24"), null, null);
+
+        reset(fibListener);
+        fibListener.update(Collections.emptyList(), Collections.singletonList(
+                new FibUpdate(FibUpdate.Type.DELETE, deleteFibEntry)));
+
+        replay(fibListener);
+
+        router.processRouteUpdates(Collections.singletonList(
+                new RouteUpdate(RouteUpdate.Type.DELETE, deleteRouteEntry)));
+
+        verify(fibListener);
+    }
+
+    /**
+     * Tests adding a route whose next hop is the local BGP speaker.
+     */
+    @Test
+    public void testLocalRouteAdd() {
+        // Construct a route entry, the next hop is the local BGP speaker
+        RouteEntry routeEntry = new RouteEntry(
+                Ip4Prefix.valueOf("1.1.1.0/24"),
+                Ip4Address.valueOf("0.0.0.0"));
+
+        // No methods on the FIB listener should be called
+        replay(fibListener);
+
+        // Call the processRouteUpdates() method in Router class
+        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
+                                                  routeEntry);
+        router.processRouteUpdates(Collections.singletonList(routeUpdate));
+
+        // Verify
+        assertEquals(1, router.getRoutes4().size());
+        assertTrue(router.getRoutes4().contains(routeEntry));
+        verify(fibListener);
+    }
+}
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/AsPathTest.java b/apps/routing/src/test/java/org/onosproject/routing/bgp/AsPathTest.java
similarity index 98%
rename from apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/AsPathTest.java
rename to apps/routing/src/test/java/org/onosproject/routing/bgp/AsPathTest.java
index 481ca43..2414eb2 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/AsPathTest.java
+++ b/apps/routing/src/test/java/org/onosproject/routing/bgp/AsPathTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
+
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+import java.util.ArrayList;
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertThat;
 
-import java.util.ArrayList;
-
-import org.junit.Test;
-
 /**
  * Unit tests for the BgpRouteEntry.AsPath class.
  */
@@ -211,7 +212,7 @@
         //
         BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments);
 
-        assertThat(asPath1, is(not(asPath2)));
+        assertThat(asPath1, Matchers.is(not(asPath2)));
     }
 
     /**
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpRouteEntryTest.java b/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpRouteEntryTest.java
similarity index 94%
rename from apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpRouteEntryTest.java
rename to apps/routing/src/test/java/org/onosproject/routing/bgp/BgpRouteEntryTest.java
index d53eb59..ab72b6f 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpRouteEntryTest.java
+++ b/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpRouteEntryTest.java
@@ -13,22 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
 
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertThat;
-
-import java.util.ArrayList;
-
+import org.easymock.EasyMock;
+import org.hamcrest.Matchers;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 
+import java.util.ArrayList;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
 /**
  * Unit tests for the BgpRouteEntry class.
  */
@@ -63,9 +62,9 @@
     @Before
     public void setUp() throws Exception {
         // Mock objects for testing
-        bgpSession = createMock(BgpSession.class);
-        bgpSession2 = createMock(BgpSession.class);
-        bgpSession3 = createMock(BgpSession.class);
+        bgpSession = EasyMock.createMock(BgpSession.class);
+        bgpSession2 = EasyMock.createMock(BgpSession.class);
+        bgpSession3 = EasyMock.createMock(BgpSession.class);
 
         // Setup the BGP Sessions
         remoteInfo.setIp4Address(BGP_SESSION_IP_ADDRESS);
@@ -75,16 +74,16 @@
         remoteInfo2.setBgpId(BGP_SESSION_BGP_ID2);
         remoteInfo3.setBgpId(BGP_SESSION_BGP_ID3);
 
-        expect(bgpSession.localInfo()).andReturn(localInfo).anyTimes();
-        expect(bgpSession.remoteInfo()).andReturn(remoteInfo).anyTimes();
-        expect(bgpSession2.localInfo()).andReturn(localInfo2).anyTimes();
-        expect(bgpSession2.remoteInfo()).andReturn(remoteInfo2).anyTimes();
-        expect(bgpSession3.localInfo()).andReturn(localInfo3).anyTimes();
-        expect(bgpSession3.remoteInfo()).andReturn(remoteInfo3).anyTimes();
+        EasyMock.expect(bgpSession.localInfo()).andReturn(localInfo).anyTimes();
+        EasyMock.expect(bgpSession.remoteInfo()).andReturn(remoteInfo).anyTimes();
+        EasyMock.expect(bgpSession2.localInfo()).andReturn(localInfo2).anyTimes();
+        EasyMock.expect(bgpSession2.remoteInfo()).andReturn(remoteInfo2).anyTimes();
+        EasyMock.expect(bgpSession3.localInfo()).andReturn(localInfo3).anyTimes();
+        EasyMock.expect(bgpSession3.remoteInfo()).andReturn(remoteInfo3).anyTimes();
 
-        replay(bgpSession);
-        replay(bgpSession2);
-        replay(bgpSession3);
+        EasyMock.replay(bgpSession);
+        EasyMock.replay(bgpSession2);
+        EasyMock.replay(bgpSession3);
     }
 
     /**
@@ -500,7 +499,7 @@
                               localPref);
         bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
 
-        assertThat(bgpRouteEntry1, is(not(bgpRouteEntry2)));
+        assertThat(bgpRouteEntry1, Matchers.is(not(bgpRouteEntry2)));
     }
 
     /**
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java b/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java
similarity index 98%
rename from apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
rename to apps/routing/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java
index 7ae8658..c73eaff 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/BgpSessionManagerTest.java
+++ b/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java
@@ -13,22 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
 
-import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
+import com.google.common.net.InetAddresses;
 import org.hamcrest.Description;
 import org.hamcrest.TypeSafeMatcher;
 import org.jboss.netty.bootstrap.ClientBootstrap;
@@ -44,12 +31,24 @@
 import org.junit.Test;
 import org.onlab.junit.TestUtils;
 import org.onlab.junit.TestUtils.TestUtilsException;
-import org.onosproject.sdnip.RouteListener;
-import org.onosproject.sdnip.RouteUpdate;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
+import org.onosproject.routingapi.RouteListener;
+import org.onosproject.routingapi.RouteUpdate;
 
-import com.google.common.net.InetAddresses;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
 
 /**
  * Unit tests for the BgpSessionManager class.
@@ -251,9 +250,9 @@
         // Setup the BGP Session Manager to test, and start listening for BGP
         // connections.
         //
-        bgpSessionManager = new BgpSessionManager(dummyRouteListener);
+        bgpSessionManager = new BgpSessionManager();
         // NOTE: We use port 0 to bind on any available port
-        bgpSessionManager.start(0);
+        bgpSessionManager.start(dummyRouteListener, 0);
 
         // Get the port number the BGP Session Manager is listening on
         Channel serverChannel = TestUtils.getField(bgpSessionManager,
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/PathSegmentTest.java b/apps/routing/src/test/java/org/onosproject/routing/bgp/PathSegmentTest.java
similarity index 96%
rename from apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/PathSegmentTest.java
rename to apps/routing/src/test/java/org/onosproject/routing/bgp/PathSegmentTest.java
index e8f2ddc..4c6c5fc 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/PathSegmentTest.java
+++ b/apps/routing/src/test/java/org/onosproject/routing/bgp/PathSegmentTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
+package org.onosproject.routing.bgp;
+
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+import java.util.ArrayList;
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertThat;
 
-import java.util.ArrayList;
-
-import org.junit.Test;
-
 /**
  * Unit tests for the BgpRouteEntry.PathSegment class.
  */
@@ -113,7 +114,7 @@
         BgpRouteEntry.PathSegment pathSegment2 =
             new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
 
-        assertThat(pathSegment1, is(not(pathSegment2)));
+        assertThat(pathSegment1, Matchers.is(not(pathSegment2)));
     }
 
     /**
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerChannelHandler.java b/apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerChannelHandler.java
similarity index 99%
rename from apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerChannelHandler.java
rename to apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerChannelHandler.java
index 979a1ed..ac887aa 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerChannelHandler.java
+++ b/apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerChannelHandler.java
@@ -13,9 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
-
-import java.util.Collection;
+package org.onosproject.routing.bgp;
 
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
@@ -25,6 +23,8 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 
+import java.util.Collection;
+
 /**
  * Class for handling the remote BGP Peer session.
  */
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerFrameDecoder.java b/apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerFrameDecoder.java
similarity index 98%
rename from apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerFrameDecoder.java
rename to apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerFrameDecoder.java
index 4f39547..9747ea9 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/bgp/TestBgpPeerFrameDecoder.java
+++ b/apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerFrameDecoder.java
@@ -13,9 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.sdnip.bgp;
-
-import java.util.concurrent.CountDownLatch;
+package org.onosproject.routing.bgp;
 
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.channel.Channel;
@@ -23,6 +21,8 @@
 import org.jboss.netty.handler.codec.frame.FrameDecoder;
 import org.onlab.packet.Ip4Address;
 
+import java.util.concurrent.CountDownLatch;
+
 /**
  * Class for handling the decoding of the BGP messages at the remote
  * BGP peer session.
diff --git a/apps/sdnip/pom.xml b/apps/sdnip/pom.xml
index 920b5e5..925bc58 100644
--- a/apps/sdnip/pom.xml
+++ b/apps/sdnip/pom.xml
@@ -61,11 +61,6 @@
 
     <dependency>
       <groupId>org.onosproject</groupId>
-      <artifactId>onlab-thirdparty</artifactId>
-    </dependency>
-
-    <dependency>
-      <groupId>org.onosproject</groupId>
       <artifactId>onlab-misc</artifactId>
     </dependency>
 
@@ -85,13 +80,13 @@
 
     <dependency>
       <groupId>org.onosproject</groupId>
-      <artifactId>onos-cli</artifactId>
+      <artifactId>onos-app-routing-api</artifactId>
       <version>${project.version}</version>
     </dependency>
 
     <dependency>
       <groupId>org.onosproject</groupId>
-      <artifactId>onos-core-dist</artifactId>
+      <artifactId>onos-cli</artifactId>
       <version>${project.version}</version>
     </dependency>
 
@@ -107,11 +102,6 @@
     </dependency>
 
     <dependency>
-      <groupId>org.osgi</groupId>
-      <artifactId>org.osgi.core</artifactId>
-    </dependency>
-
-    <dependency>
       <groupId>org.easymock</groupId>
       <artifactId>easymock</artifactId>
       <scope>test</scope>
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
index 9f3c872..698a2d6 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
@@ -36,6 +36,8 @@
 import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
 import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.routingapi.FibListener;
+import org.onosproject.routingapi.FibUpdate;
 import org.onosproject.sdnip.config.BgpPeer;
 import org.onosproject.sdnip.config.Interface;
 import org.onosproject.sdnip.config.SdnIpConfigurationService;
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
index 59ff346..93b442d 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
@@ -15,15 +15,11 @@
  */
 package org.onosproject.sdnip;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.IPv6;
-import org.onlab.packet.IpAddress;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
@@ -32,7 +28,6 @@
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.intent.PointToPointIntent;
-import org.onosproject.sdnip.bgp.BgpConstants;
 import org.onosproject.sdnip.config.BgpPeer;
 import org.onosproject.sdnip.config.BgpSpeaker;
 import org.onosproject.sdnip.config.Interface;
@@ -41,6 +36,10 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
 /**
  * Manages the connectivity requirements between peers.
  */
@@ -49,6 +48,8 @@
     private static final Logger log = LoggerFactory.getLogger(
             PeerConnectivityManager.class);
 
+    private static final short BGP_PORT = 179;
+
     private final IntentSynchronizer intentSynchronizer;
     private final SdnIpConfigurationService configService;
     private final InterfaceService interfaceService;
@@ -191,7 +192,7 @@
                                  bgpdAddress,
                                  bgpdPeerAddress,
                                  null,
-                                 (short) BgpConstants.BGP_PORT);
+                                 BGP_PORT);
 
         intents.add(new PointToPointIntent(appId, selector, treatment,
                                bgpdConnectPoint, bgpdPeerConnectPoint));
@@ -200,7 +201,7 @@
         selector = buildSelector(tcpProtocol,
                                  bgpdAddress,
                                  bgpdPeerAddress,
-                                 (short) BgpConstants.BGP_PORT,
+                                 BGP_PORT,
                                  null);
 
         intents.add(new PointToPointIntent(appId, selector, treatment,
@@ -211,7 +212,7 @@
                                  bgpdPeerAddress,
                                  bgpdAddress,
                                  null,
-                                 (short) BgpConstants.BGP_PORT);
+                                 BGP_PORT);
 
         intents.add(new PointToPointIntent(appId, selector, treatment,
                                bgpdPeerConnectPoint, bgpdConnectPoint));
@@ -220,7 +221,7 @@
         selector = buildSelector(tcpProtocol,
                                  bgpdPeerAddress,
                                  bgpdAddress,
-                                 (short) BgpConstants.BGP_PORT,
+                                 BGP_PORT,
                                  null);
 
         intents.add(new PointToPointIntent(appId, selector, treatment,
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
index 0b921ff..b375852 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
@@ -32,14 +32,11 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.intent.IntentService;
-import org.onosproject.sdnip.bgp.BgpRouteEntry;
-import org.onosproject.sdnip.bgp.BgpSession;
-import org.onosproject.sdnip.bgp.BgpSessionManager;
+import org.onosproject.routingapi.RoutingService;
 import org.onosproject.sdnip.config.SdnIpConfigurationReader;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
-import java.util.Collection;
 import java.util.Dictionary;
 
 import static org.slf4j.LoggerFactory.getLogger;
@@ -69,8 +66,11 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected LeadershipService leadershipService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RoutingService routingService;
+
     //
-    // NOTE: Unused reference - needed to guarentee that the
+    // NOTE: Unused reference - needed to guarantee that the
     // NetworkConfigReader component is activated and the network configuration
     // is read.
     //
@@ -83,8 +83,7 @@
     private IntentSynchronizer intentSynchronizer;
     private SdnIpConfigurationReader config;
     private PeerConnectivityManager peerConnectivity;
-    private Router router;
-    private BgpSessionManager bgpSessionManager;
+
     private LeadershipEventListener leadershipEventListener =
         new InnerLeadershipEventListener();
     private ApplicationId appId;
@@ -114,23 +113,18 @@
                                                        interfaceService);
         peerConnectivity.start();
 
-        router = new Router(intentSynchronizer, hostService);
-        router.start();
+        routingService.start(intentSynchronizer);
 
         leadershipService.addListener(leadershipEventListener);
         leadershipService.runForLeadership(appId.name());
 
         log.info("Starting BGP with port {}", bgpPort);
-
-        bgpSessionManager = new BgpSessionManager(router);
-        bgpSessionManager.start(bgpPort);
+        // TODO feed port information through to the BgpService
     }
 
     @Deactivate
     protected void deactivate() {
-
-        bgpSessionManager.stop();
-        router.stop();
+        routingService.stop();
         peerConnectivity.stop();
         intentSynchronizer.stop();
 
@@ -168,31 +162,6 @@
     }
 
     @Override
-    public Collection<BgpSession> getBgpSessions() {
-        return bgpSessionManager.getBgpSessions();
-    }
-
-    @Override
-    public Collection<BgpRouteEntry> getBgpRoutes4() {
-        return bgpSessionManager.getBgpRoutes4();
-    }
-
-    @Override
-    public Collection<BgpRouteEntry> getBgpRoutes6() {
-        return bgpSessionManager.getBgpRoutes6();
-    }
-
-    @Override
-    public Collection<RouteEntry> getRoutes4() {
-        return router.getRoutes4();
-    }
-
-    @Override
-    public Collection<RouteEntry> getRoutes6() {
-        return router.getRoutes6();
-    }
-
-    @Override
     public void modifyPrimary(boolean isPrimary) {
         intentSynchronizer.leaderChanged(isPrimary);
     }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java
index aa71f02..dbb0d88 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpService.java
@@ -15,49 +15,10 @@
  */
 package org.onosproject.sdnip;
 
-import java.util.Collection;
-
-import org.onosproject.sdnip.bgp.BgpRouteEntry;
-import org.onosproject.sdnip.bgp.BgpSession;
-
 /**
  * Service interface exported by SDN-IP.
  */
 public interface SdnIpService {
-    /**
-     * Gets the BGP sessions.
-     *
-     * @return the BGP sessions
-     */
-    public Collection<BgpSession> getBgpSessions();
-
-    /**
-     * Gets the selected IPv4 BGP routes among all BGP sessions.
-     *
-     * @return the selected IPv4 BGP routes among all BGP sessions
-     */
-    public Collection<BgpRouteEntry> getBgpRoutes4();
-
-    /**
-     * Gets the selected IPv6 BGP routes among all BGP sessions.
-     *
-     * @return the selected IPv6 BGP routes among all BGP sessions
-     */
-    public Collection<BgpRouteEntry> getBgpRoutes6();
-
-    /**
-     * Gets all IPv4 routes known to SDN-IP.
-     *
-     * @return the SDN-IP IPv4 routes
-     */
-    public Collection<RouteEntry> getRoutes4();
-
-    /**
-     * Gets all IPv6 routes known to SDN-IP.
-     *
-     * @return the SDN-IP IPv6 routes
-     */
-    public Collection<RouteEntry> getRoutes6();
 
     /**
      * Changes whether this SDN-IP instance is the primary or not based on the
diff --git a/apps/sdnip/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/sdnip/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index dd33f73..3be1c79 100644
--- a/apps/sdnip/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/sdnip/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -17,15 +17,6 @@
 
   <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
     <command>
-      <action class="org.onosproject.sdnip.cli.BgpNeighborsListCommand"/>
-    </command>
-    <command>
-      <action class="org.onosproject.sdnip.cli.BgpRoutesListCommand"/>
-    </command>
-    <command>
-      <action class="org.onosproject.sdnip.cli.RoutesListCommand"/>
-    </command>
-    <command>
       <action class="org.onosproject.sdnip.cli.PrimaryChangeCommand"/>
     </command>
   </command-bundle>
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
index e4808f7..d13aa2a 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
@@ -16,9 +16,8 @@
 package org.onosproject.sdnip;
 
 import com.google.common.collect.Sets;
-import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
-import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
-import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
+import org.easymock.EasyMock;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.junit.TestUtils;
@@ -32,18 +31,12 @@
 import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultHost;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostListener;
-import org.onosproject.net.host.HostService;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.intent.AbstractIntentTest;
 import org.onosproject.net.intent.Intent;
@@ -51,10 +44,18 @@
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.provider.ProviderId;
+import org.onosproject.routingapi.FibEntry;
+import org.onosproject.routingapi.FibUpdate;
+import org.onosproject.routingapi.RouteEntry;
+import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
+import org.onosproject.sdnip.config.BgpPeer;
 import org.onosproject.sdnip.config.Interface;
+import org.onosproject.sdnip.config.SdnIpConfigurationService;
 
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -71,9 +72,9 @@
  */
 public class IntentSyncTest extends AbstractIntentTest {
 
+    private SdnIpConfigurationService sdnIpConfigService;
     private InterfaceService interfaceService;
     private IntentService intentService;
-    private HostService hostService;
 
     private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
             DeviceId.deviceId("of:0000000000000001"),
@@ -87,8 +88,11 @@
             DeviceId.deviceId("of:0000000000000003"),
             PortNumber.portNumber(1));
 
+    private static final ConnectPoint SW4_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000004"),
+            PortNumber.portNumber(1));
+
     private IntentSynchronizer intentSynchronizer;
-    private Router router;
 
     private static final ApplicationId APPID = new ApplicationId() {
         @Override
@@ -106,12 +110,42 @@
     public void setUp() throws Exception {
         super.setUp();
         setUpInterfaceService();
-        setUpHostService();
+
+        setUpBgpPeers();
         intentService = createMock(IntentService.class);
 
         intentSynchronizer = new IntentSynchronizer(APPID, intentService,
-                                                    null, interfaceService);
-        router = new Router(intentSynchronizer, hostService);
+                                                    sdnIpConfigService, interfaceService);
+    }
+
+    /**
+     * Sets up BGP peers in external networks.
+     */
+    private void setUpBgpPeers() {
+
+        Map<IpAddress, BgpPeer> peers = new HashMap<>();
+
+        String peerSw1Eth1 = "192.168.10.1";
+        peers.put(IpAddress.valueOf(peerSw1Eth1),
+                  new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
+
+        // Two BGP peers are connected to switch 2 port 1.
+        String peer1Sw2Eth1 = "192.168.20.1";
+        peers.put(IpAddress.valueOf(peer1Sw2Eth1),
+                  new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
+
+        String peer2Sw2Eth1 = "192.168.20.2";
+        peers.put(IpAddress.valueOf(peer2Sw2Eth1),
+                  new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
+
+        String peer1Sw4Eth1 = "192.168.40.1";
+        peers.put(IpAddress.valueOf(peer1Sw4Eth1),
+                  new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1));
+
+        sdnIpConfigService = createMock(SdnIpConfigurationService.class);
+        expect(sdnIpConfigService.getBgpPeers()).andReturn(peers).anyTimes();
+        EasyMock.replay(sdnIpConfigService);
+
     }
 
     /**
@@ -133,80 +167,279 @@
         interfaces.add(sw1Eth1);
 
         Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet();
-        interfaceIpAddresses2.add(new InterfaceIpAddress(
-                IpAddress.valueOf("192.168.20.101"),
-                IpPrefix.valueOf("192.168.20.0/24")));
+        interfaceIpAddresses2.add(
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.20.101"),
+                                       IpPrefix.valueOf("192.168.20.0/24")));
         Interface sw2Eth1 = new Interface(SW2_ETH1,
                 interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"),
                 VlanId.NONE);
         interfaces.add(sw2Eth1);
 
         Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet();
-        interfaceIpAddresses3.add(new InterfaceIpAddress(
-                IpAddress.valueOf("192.168.30.101"),
-                IpPrefix.valueOf("192.168.30.0/24")));
+        interfaceIpAddresses3.add(
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.30.101"),
+                                       IpPrefix.valueOf("192.168.30.0/24")));
         Interface sw3Eth1 = new Interface(SW3_ETH1,
                 interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"),
                 VlanId.NONE);
         interfaces.add(sw3Eth1);
 
+        InterfaceIpAddress interfaceIpAddress4 =
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"),
+                                       IpPrefix.valueOf("192.168.40.0/24"));
+        Interface sw4Eth1 = new Interface(SW4_ETH1,
+                                          Sets.newHashSet(interfaceIpAddress4),
+                                          MacAddress.valueOf("00:00:00:00:00:04"),
+                                          VlanId.vlanId((short) 1));
+
+        expect(interfaceService.getInterface(SW4_ETH1)).andReturn(sw4Eth1).anyTimes();
+        interfaces.add(sw4Eth1);
+
         expect(interfaceService.getInterface(SW1_ETH1)).andReturn(
                 sw1Eth1).anyTimes();
         expect(interfaceService.getInterface(SW2_ETH1)).andReturn(
                 sw2Eth1).anyTimes();
         expect(interfaceService.getInterface(SW3_ETH1)).andReturn(
                 sw3Eth1).anyTimes();
-        expect(interfaceService.getInterfaces()).andReturn(
-                interfaces).anyTimes();
+        expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
         replay(interfaceService);
     }
 
     /**
-     * Sets up the host service with details of hosts.
+     * Tests adding a FIB entry to the IntentSynchronizer.
+     *
+     * We verify that the synchronizer records the correct state and that the
+     * correct intent is submitted to the IntentService.
+     *
+     * @throws TestUtilsException
      */
-    private void setUpHostService() {
-        hostService = createMock(HostService.class);
+    @Test
+    public void testFibAdd() throws TestUtilsException {
+        FibEntry fibEntry = new FibEntry(
+                Ip4Prefix.valueOf("1.1.1.0/24"),
+                Ip4Address.valueOf("192.168.10.1"),
+                MacAddress.valueOf("00:00:00:00:00:01"));
 
-        hostService.addListener(anyObject(HostListener.class));
-        expectLastCall().anyTimes();
+        // Construct a MultiPointToSinglePointIntent intent
+        TrafficSelector.Builder selectorBuilder =
+                DefaultTrafficSelector.builder();
+        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+                fibEntry.prefix());
 
-        IpAddress host1Address = IpAddress.valueOf("192.168.10.1");
-        Host host1 = new DefaultHost(ProviderId.NONE, HostId.NONE,
-                MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE,
-                new HostLocation(SW1_ETH1, 1),
-                        Sets.newHashSet(host1Address));
+        TrafficTreatment.Builder treatmentBuilder =
+                DefaultTrafficTreatment.builder();
+        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
 
-        expect(hostService.getHostsByIp(host1Address))
-                .andReturn(Sets.newHashSet(host1)).anyTimes();
-        hostService.startMonitoringIp(host1Address);
-        expectLastCall().anyTimes();
+        Set<ConnectPoint> ingressPoints = new HashSet<>();
+        ingressPoints.add(SW2_ETH1);
+        ingressPoints.add(SW3_ETH1);
+        ingressPoints.add(SW4_ETH1);
+
+        MultiPointToSinglePointIntent intent =
+                new MultiPointToSinglePointIntent(APPID,
+                                                  selectorBuilder.build(), treatmentBuilder.build(),
+                                                  ingressPoints, SW1_ETH1);
+
+        // Setup the expected intents
+        IntentOperations.Builder builder = IntentOperations.builder(APPID);
+        builder.addSubmitOperation(intent);
+        intentService.execute(TestIntentServiceHelper.eqExceptId(
+                builder.build()));
+        replay(intentService);
+
+        intentSynchronizer.leaderChanged(true);
+        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
+
+        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
+                                            fibEntry);
+        intentSynchronizer.update(Collections.singleton(fibUpdate),
+                                  Collections.emptyList());
+
+        Assert.assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
+        Intent firstIntent =
+                intentSynchronizer.getRouteIntents().iterator().next();
+        IntentKey firstIntentKey = new IntentKey(firstIntent);
+        IntentKey intentKey = new IntentKey(intent);
+        assertTrue(firstIntentKey.equals(intentKey));
+        verify(intentService);
+    }
+
+    /**
+     * Tests adding a FIB entry with to a next hop in a VLAN.
+     *
+     * We verify that the synchronizer records the correct state and that the
+     * correct intent is submitted to the IntentService.
+     *
+     * @throws TestUtilsException
+     */
+    @Test
+    public void testFibAddWithVlan() throws TestUtilsException {
+        FibEntry fibEntry = new FibEntry(
+                Ip4Prefix.valueOf("3.3.3.0/24"),
+                Ip4Address.valueOf("192.168.40.1"),
+                MacAddress.valueOf("00:00:00:00:00:04"));
+
+        // Construct a MultiPointToSinglePointIntent intent
+        TrafficSelector.Builder selectorBuilder =
+                DefaultTrafficSelector.builder();
+        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                       .matchIPDst(fibEntry.prefix())
+                       .matchVlanId(VlanId.ANY);
+
+        TrafficTreatment.Builder treatmentBuilder =
+                DefaultTrafficTreatment.builder();
+        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:04"))
+                        .setVlanId(VlanId.vlanId((short) 1));
+
+        Set<ConnectPoint> ingressPoints = new HashSet<>();
+        ingressPoints.add(SW1_ETH1);
+        ingressPoints.add(SW2_ETH1);
+        ingressPoints.add(SW3_ETH1);
+
+        MultiPointToSinglePointIntent intent =
+                new MultiPointToSinglePointIntent(APPID,
+                        selectorBuilder.build(), treatmentBuilder.build(),
+                        ingressPoints, SW4_ETH1);
+
+        // Setup the expected intents
+        IntentOperations.Builder builder = IntentOperations.builder(APPID);
+        builder.addSubmitOperation(intent);
+        intentService.execute(
+                TestIntentServiceHelper.eqExceptId(builder.build()));
+        replay(intentService);
+
+        // Run the test
+        intentSynchronizer.leaderChanged(true);
+        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
+        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
+
+        intentSynchronizer.update(Collections.singleton(fibUpdate),
+                                  Collections.emptyList());
+
+        // Verify
+        Assert.assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
+        Intent firstIntent =
+            intentSynchronizer.getRouteIntents().iterator().next();
+        IntentKey firstIntentKey = new IntentKey(firstIntent);
+        IntentKey intentKey = new IntentKey(intent);
+        assertTrue(firstIntentKey.equals(intentKey));
+        verify(intentService);
+    }
+
+    /**
+     * Tests updating a FIB entry.
+     *
+     * We verify that the synchronizer records the correct state and that the
+     * correct intent is submitted to the IntentService.
+     *
+     * @throws TestUtilsException
+     */
+    @Test
+    public void testFibUpdate() throws TestUtilsException {
+        // Firstly add a route
+        testFibAdd();
+
+        Intent addedIntent =
+                intentSynchronizer.getRouteIntents().iterator().next();
+
+        // Start to construct a new route entry and new intent
+        FibEntry fibEntryUpdate = new FibEntry(
+                Ip4Prefix.valueOf("1.1.1.0/24"),
+                Ip4Address.valueOf("192.168.20.1"),
+                MacAddress.valueOf("00:00:00:00:00:02"));
+
+        // Construct a new MultiPointToSinglePointIntent intent
+        TrafficSelector.Builder selectorBuilderNew =
+                DefaultTrafficSelector.builder();
+        selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+                fibEntryUpdate.prefix());
+
+        TrafficTreatment.Builder treatmentBuilderNew =
+                DefaultTrafficTreatment.builder();
+        treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
 
 
-        IpAddress host2Address = IpAddress.valueOf("192.168.20.1");
-        Host host2 = new DefaultHost(ProviderId.NONE, HostId.NONE,
-                MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
-                new HostLocation(SW2_ETH1, 1),
-                        Sets.newHashSet(host2Address));
+        Set<ConnectPoint> ingressPointsNew = new HashSet<>();
+        ingressPointsNew.add(SW1_ETH1);
+        ingressPointsNew.add(SW3_ETH1);
+        ingressPointsNew.add(SW4_ETH1);
 
-        expect(hostService.getHostsByIp(host2Address))
-                .andReturn(Sets.newHashSet(host2)).anyTimes();
-        hostService.startMonitoringIp(host2Address);
-        expectLastCall().anyTimes();
+        MultiPointToSinglePointIntent intentNew =
+                new MultiPointToSinglePointIntent(APPID,
+                                                  selectorBuilderNew.build(),
+                                                  treatmentBuilderNew.build(),
+                                                  ingressPointsNew, SW2_ETH1);
 
+        // Set up test expectation
+        reset(intentService);
+        // Setup the expected intents
+        IntentOperations.Builder builder = IntentOperations.builder(APPID);
+        builder.addWithdrawOperation(addedIntent.id());
+        intentService.execute(TestIntentServiceHelper.eqExceptId(
+                builder.build()));
+        builder = IntentOperations.builder(APPID);
+        builder.addSubmitOperation(intentNew);
+        intentService.execute(TestIntentServiceHelper.eqExceptId(
+                builder.build()));
+        replay(intentService);
 
-        IpAddress host3Address = IpAddress.valueOf("192.168.30.1");
-        Host host3 = new DefaultHost(ProviderId.NONE, HostId.NONE,
-                MacAddress.valueOf("00:00:00:00:00:03"), VlanId.NONE,
-                new HostLocation(SW3_ETH1, 1),
-                        Sets.newHashSet(host3Address));
+        // Call the update() method in IntentSynchronizer class
+        intentSynchronizer.leaderChanged(true);
+        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
+        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
+                                                  fibEntryUpdate);
+        intentSynchronizer.update(Collections.singletonList(fibUpdate),
+                                  Collections.emptyList());
 
-        expect(hostService.getHostsByIp(host3Address))
-                .andReturn(Sets.newHashSet(host3)).anyTimes();
-        hostService.startMonitoringIp(host3Address);
-        expectLastCall().anyTimes();
+        // Verify
+        Assert.assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
+        Intent firstIntent =
+                intentSynchronizer.getRouteIntents().iterator().next();
+        IntentKey firstIntentKey = new IntentKey(firstIntent);
+        IntentKey intentNewKey = new IntentKey(intentNew);
+        assertTrue(firstIntentKey.equals(intentNewKey));
+        verify(intentService);
+    }
 
+    /**
+     * Tests deleting a FIB entry.
+     *
+     * We verify that the synchronizer records the correct state and that the
+     * correct intent is withdrawn from the IntentService.
+     *
+     * @throws TestUtilsException
+     */
+    @Test
+    public void testFibDelete() throws TestUtilsException {
+        // Firstly add a route
+        testFibAdd();
 
-        replay(hostService);
+        Intent addedIntent =
+                intentSynchronizer.getRouteIntents().iterator().next();
+
+        // Construct the existing route entry
+        FibEntry fibEntry = new FibEntry(
+                Ip4Prefix.valueOf("1.1.1.0/24"), null, null);
+
+        // Set up expectation
+        reset(intentService);
+        // Setup the expected intents
+        IntentOperations.Builder builder = IntentOperations.builder(APPID);
+        builder.addWithdrawOperation(addedIntent.id());
+        intentService.execute(TestIntentServiceHelper.eqExceptId(
+                builder.build()));
+        replay(intentService);
+
+        // Call the update() method in IntentSynchronizer class
+        intentSynchronizer.leaderChanged(true);
+        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
+        FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.DELETE, fibEntry);
+        intentSynchronizer.update(Collections.emptyList(),
+                                  Collections.singletonList(fibUpdate));
+
+        // Verify
+        Assert.assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
+        verify(intentService);
     }
 
     /**
@@ -287,25 +520,7 @@
         MultiPointToSinglePointIntent intent6 = intentBuilder(
                 routeEntry6.prefix(), "00:00:00:00:00:01",  SW1_ETH1);
 
-        // Set up the ribTable field in Router class and routeIntents fields
-        // in IntentSynchronizer class
-        InvertedRadixTree<RouteEntry> ribTable =
-                new ConcurrentInvertedRadixTree<>(
-                new DefaultByteArrayNodeFactory());
-        ribTable.put(RouteEntry.createBinaryString(routeEntry1.prefix()),
-                     routeEntry1);
-        ribTable.put(RouteEntry.createBinaryString(routeEntry3.prefix()),
-                     routeEntry3);
-        ribTable.put(RouteEntry.createBinaryString(routeEntry4Update.prefix()),
-                     routeEntry4Update);
-        ribTable.put(RouteEntry.createBinaryString(routeEntry5.prefix()),
-                     routeEntry5);
-        ribTable.put(RouteEntry.createBinaryString(routeEntry6.prefix()),
-                     routeEntry6);
-        ribTable.put(RouteEntry.createBinaryString(routeEntry7.prefix()),
-                     routeEntry7);
-        TestUtils.setField(router, "ribTable4", ribTable);
-
+        // Set up the routeIntents field in IntentSynchronizer class
         ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent>
             routeIntents =  new ConcurrentHashMap<>();
         routeIntents.put(routeEntry1.prefix(), intent1);
@@ -353,20 +568,9 @@
 
         // Start the test
         intentSynchronizer.leaderChanged(true);
-        /*
-        TestUtils.callMethod(intentSynchronizer, "synchronizeIntents",
-                             new Class<?>[] {});
-        */
         intentSynchronizer.synchronizeIntents();
 
         // Verify
-        assertEquals(router.getRoutes4().size(), 6);
-        assertTrue(router.getRoutes4().contains(routeEntry1));
-        assertTrue(router.getRoutes4().contains(routeEntry3));
-        assertTrue(router.getRoutes4().contains(routeEntry4Update));
-        assertTrue(router.getRoutes4().contains(routeEntry5));
-        assertTrue(router.getRoutes4().contains(routeEntry6));
-
         assertEquals(intentSynchronizer.getRouteIntents().size(), 6);
         assertTrue(intentSynchronizer.getRouteIntents().contains(intent1));
         assertTrue(intentSynchronizer.getRouteIntents().contains(intent3));
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
index 5311a72..298972d 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
@@ -41,7 +41,6 @@
 import org.onosproject.net.intent.IntentOperations;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.PointToPointIntent;
-import org.onosproject.sdnip.bgp.BgpConstants;
 import org.onosproject.sdnip.config.BgpPeer;
 import org.onosproject.sdnip.config.BgpSpeaker;
 import org.onosproject.sdnip.config.Interface;
@@ -302,7 +301,7 @@
      */
     private void setUpBgpIntents() {
 
-        Short bgpPort = Short.valueOf((short) BgpConstants.BGP_PORT);
+        Short bgpPort = 179;
 
         // Start to build intents between BGP speaker1 and BGP peer1
         bgpPathintentConstructor(
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java
deleted file mode 100644
index 4d14228..0000000
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * Copyright 2014 Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.sdnip;
-
-import com.google.common.collect.Sets;
-import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
-import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
-import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.junit.TestUtils;
-import org.onlab.junit.TestUtils.TestUtilsException;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultHost;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.intent.AbstractIntentTest;
-import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentOperations;
-import org.onosproject.net.intent.IntentService;
-import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.provider.ProviderId;
-import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
-import org.onosproject.sdnip.Router.InternalHostListener;
-import org.onosproject.sdnip.config.BgpPeer;
-import org.onosproject.sdnip.config.Interface;
-import org.onosproject.sdnip.config.SdnIpConfigurationService;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static org.easymock.EasyMock.*;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-/**
- * This class tests adding a route, updating a route, deleting a route, and
- * the ARP module answers the MAC address asynchronously.
- */
-public class RouterAsyncArpTest extends AbstractIntentTest {
-
-    private SdnIpConfigurationService sdnIpConfigService;
-    private InterfaceService interfaceService;
-    private IntentService intentService;
-    private HostService hostService;
-
-    private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
-            DeviceId.deviceId("of:0000000000000001"),
-            PortNumber.portNumber(1));
-
-    private static final ConnectPoint SW2_ETH1 = new ConnectPoint(
-            DeviceId.deviceId("of:0000000000000002"),
-            PortNumber.portNumber(1));
-
-    private static final ConnectPoint SW3_ETH1 = new ConnectPoint(
-            DeviceId.deviceId("of:0000000000000003"),
-            PortNumber.portNumber(1));
-
-    private IntentSynchronizer intentSynchronizer;
-    private Router router;
-    private InternalHostListener internalHostListener;
-
-    private static final ApplicationId APPID = new ApplicationId() {
-        @Override
-        public short id() {
-            return 1;
-        }
-
-        @Override
-        public String name() {
-            return "SDNIP";
-        }
-    };
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-
-        setUpSdnIpConfigService();
-        setUpInterfaceService();
-        hostService = createMock(HostService.class);
-        intentService = createMock(IntentService.class);
-
-        intentSynchronizer = new IntentSynchronizer(APPID, intentService,
-                                                    sdnIpConfigService,
-                                                    interfaceService);
-        router = new Router(intentSynchronizer, hostService);
-        internalHostListener = router.new InternalHostListener();
-    }
-
-    /**
-     * Sets up SdnIpConfigService.
-     */
-    private void setUpSdnIpConfigService() {
-
-        sdnIpConfigService = createMock(SdnIpConfigurationService.class);
-
-        Map<IpAddress, BgpPeer> peers = new HashMap<>();
-
-        String peerSw1Eth1 = "192.168.10.1";
-        peers.put(IpAddress.valueOf(peerSw1Eth1),
-                new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
-
-        // Two BGP peers are connected to switch 2 port 1.
-        String peer1Sw2Eth1 = "192.168.20.1";
-        peers.put(IpAddress.valueOf(peer1Sw2Eth1),
-                new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
-
-        String peer2Sw2Eth1 = "192.168.20.2";
-        peers.put(IpAddress.valueOf(peer2Sw2Eth1),
-                new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
-
-        expect(sdnIpConfigService.getBgpPeers()).andReturn(peers).anyTimes();
-        replay(sdnIpConfigService);
-    }
-
-    /**
-     * Sets up InterfaceService.
-     */
-    private void setUpInterfaceService() {
-
-        interfaceService = createMock(InterfaceService.class);
-
-        Set<Interface> interfaces = Sets.newHashSet();
-
-        Set<InterfaceIpAddress> interfaceIpAddresses1 = Sets.newHashSet();
-        interfaceIpAddresses1.add(new InterfaceIpAddress(
-                IpAddress.valueOf("192.168.10.101"),
-                IpPrefix.valueOf("192.168.10.0/24")));
-        Interface sw1Eth1 = new Interface(SW1_ETH1,
-                interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"),
-                VlanId.NONE);
-        interfaces.add(sw1Eth1);
-
-        Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet();
-        interfaceIpAddresses2.add(new InterfaceIpAddress(
-                IpAddress.valueOf("192.168.20.101"),
-                IpPrefix.valueOf("192.168.20.0/24")));
-        Interface sw2Eth1 = new Interface(SW2_ETH1,
-                interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"),
-                VlanId.NONE);
-        interfaces.add(sw2Eth1);
-
-        Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet();
-        interfaceIpAddresses3.add(new InterfaceIpAddress(
-                IpAddress.valueOf("192.168.30.101"),
-                IpPrefix.valueOf("192.168.30.0/24")));
-        Interface sw3Eth1 = new Interface(SW3_ETH1,
-                interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"),
-                VlanId.NONE);
-        interfaces.add(sw3Eth1);
-
-        expect(interfaceService.getInterface(SW1_ETH1)).andReturn(sw1Eth1).anyTimes();
-        expect(interfaceService.getInterface(SW2_ETH1)).andReturn(sw2Eth1).anyTimes();
-        expect(interfaceService.getInterface(SW3_ETH1)).andReturn(sw3Eth1).anyTimes();
-        expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
-        replay(interfaceService);
-    }
-
-    /**
-     * This method tests adding a route entry.
-     */
-    @Test
-    public void testRouteAdd() throws TestUtilsException {
-
-        // Construct a route entry
-        RouteEntry routeEntry = new RouteEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"),
-                Ip4Address.valueOf("192.168.10.1"));
-
-        // Construct a route intent
-        MultiPointToSinglePointIntent intent = staticIntentBuilder();
-
-        // Set up test expectation
-        reset(hostService);
-        expect(hostService.getHostsByIp(anyObject(IpAddress.class))).andReturn(
-                new HashSet<Host>()).anyTimes();
-        hostService.startMonitoringIp(IpAddress.valueOf("192.168.10.1"));
-        replay(hostService);
-
-        reset(intentService);
-        IntentOperations.Builder builder = IntentOperations.builder(APPID);
-        builder.addSubmitOperation(intent);
-        intentService.execute(TestIntentServiceHelper.eqExceptId(
-                                builder.build()));
-        replay(intentService);
-
-        // Call the processRouteUpdates() method in Router class
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
-                                                  routeEntry);
-        router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
-
-        Host host = new DefaultHost(ProviderId.NONE, HostId.NONE,
-                MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE,
-                new HostLocation(
-                        SW1_ETH1.deviceId(),
-                        SW1_ETH1.port(), 1),
-                        Sets.newHashSet(IpAddress.valueOf("192.168.10.1")));
-        internalHostListener.event(
-                new HostEvent(HostEvent.Type.HOST_ADDED, host));
-
-        // Verify
-        assertEquals(router.getRoutes4().size(), 1);
-        assertTrue(router.getRoutes4().contains(routeEntry));
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
-        Intent firstIntent =
-            intentSynchronizer.getRouteIntents().iterator().next();
-        IntentKey firstIntentKey = new IntentKey(firstIntent);
-        IntentKey intentKey = new IntentKey(intent);
-        assertTrue(firstIntentKey.equals(intentKey));
-        verify(intentService);
-        verify(hostService);
-
-    }
-
-    /**
-     * This method tests updating a route entry.
-     *
-     * @throws TestUtilsException
-     */
-    @Test
-    public void testRouteUpdate() throws TestUtilsException {
-
-        // Construct the existing route entry
-        RouteEntry routeEntry = new RouteEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"),
-                Ip4Address.valueOf("192.168.10.1"));
-
-        // Construct the existing MultiPointToSinglePointIntent intent
-        MultiPointToSinglePointIntent intent = staticIntentBuilder();
-
-        // Set up the ribTable field of Router class with existing route, and
-        // routeIntents field with the corresponding existing intent
-        setRibTableField(routeEntry);
-        setRouteIntentsField(routeEntry, intent);
-
-        // Start to construct a new route entry and new intent
-        RouteEntry routeEntryUpdate = new RouteEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"),
-                Ip4Address.valueOf("192.168.20.1"));
-
-        // Construct a new MultiPointToSinglePointIntent intent
-        TrafficSelector.Builder selectorBuilderNew =
-                DefaultTrafficSelector.builder();
-        selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
-                routeEntryUpdate.prefix());
-
-        TrafficTreatment.Builder treatmentBuilderNew =
-                DefaultTrafficTreatment.builder();
-        treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
-
-        Set<ConnectPoint> ingressPointsNew = new HashSet<ConnectPoint>();
-        ingressPointsNew.add(SW1_ETH1);
-        ingressPointsNew.add(SW3_ETH1);
-
-        MultiPointToSinglePointIntent intentNew =
-                new MultiPointToSinglePointIntent(APPID,
-                        selectorBuilderNew.build(),
-                        treatmentBuilderNew.build(),
-                        ingressPointsNew, SW2_ETH1);
-
-        // Set up test expectation
-        reset(hostService);
-        expect(hostService.getHostsByIp(anyObject(IpAddress.class))).andReturn(
-                new HashSet<Host>()).anyTimes();
-        hostService.startMonitoringIp(IpAddress.valueOf("192.168.20.1"));
-        replay(hostService);
-
-        reset(intentService);
-        IntentOperations.Builder builder = IntentOperations.builder(APPID);
-        builder.addWithdrawOperation(intent.id());
-        intentService.execute(TestIntentServiceHelper.eqExceptId(
-                                builder.build()));
-        builder = IntentOperations.builder(APPID);
-        builder.addSubmitOperation(intentNew);
-        intentService.execute(TestIntentServiceHelper.eqExceptId(
-                                builder.build()));
-        replay(intentService);
-
-        // Call the processRouteUpdates() method in Router class
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
-                                                  routeEntryUpdate);
-        router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
-
-        Host host = new DefaultHost(ProviderId.NONE, HostId.NONE,
-                MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
-                new HostLocation(
-                        SW2_ETH1.deviceId(),
-                        SW2_ETH1.port(), 1),
-                        Sets.newHashSet(IpAddress.valueOf("192.168.20.1")));
-        internalHostListener.event(
-                new HostEvent(HostEvent.Type.HOST_ADDED, host));
-
-        // Verify
-        assertEquals(router.getRoutes4().size(), 1);
-        assertTrue(router.getRoutes4().contains(routeEntryUpdate));
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
-        Intent firstIntent =
-            intentSynchronizer.getRouteIntents().iterator().next();
-        IntentKey firstIntentKey = new IntentKey(firstIntent);
-        IntentKey intentNewKey = new IntentKey(intentNew);
-        assertTrue(firstIntentKey.equals(intentNewKey));
-        verify(intentService);
-        verify(hostService);
-    }
-
-    /**
-     * This method tests deleting a route entry.
-     */
-    @Test
-    public void testRouteDelete() throws TestUtilsException {
-
-        // Construct the existing route entry
-        RouteEntry routeEntry = new RouteEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"),
-                Ip4Address.valueOf("192.168.10.1"));
-
-        // Construct the existing MultiPointToSinglePointIntent intent
-        MultiPointToSinglePointIntent intent = staticIntentBuilder();
-
-        // Set up the ribTable field of Router class with existing route, and
-        // routeIntents field with the corresponding existing intent
-        setRibTableField(routeEntry);
-        setRouteIntentsField(routeEntry, intent);
-
-        // Set up expectation
-        reset(intentService);
-        IntentOperations.Builder builder = IntentOperations.builder(APPID);
-        builder.addWithdrawOperation(intent.id());
-        intentService.execute(TestIntentServiceHelper.eqExceptId(
-                                builder.build()));
-        replay(intentService);
-
-        // Call the processRouteUpdates() method in Router class
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.DELETE,
-                                                  routeEntry);
-        router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
-
-        // Verify
-        assertEquals(router.getRoutes4().size(), 0);
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
-        verify(intentService);
-    }
-
-    /**
-     * Constructs a static MultiPointToSinglePointIntent.
-     */
-    private MultiPointToSinglePointIntent staticIntentBuilder() {
-
-        TrafficSelector.Builder selectorBuilder =
-                DefaultTrafficSelector.builder();
-        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
-                IpPrefix.valueOf("1.1.1.0/24"));
-
-        TrafficTreatment.Builder treatmentBuilder =
-                DefaultTrafficTreatment.builder();
-        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
-
-        Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
-        ingressPoints.add(SW2_ETH1);
-        ingressPoints.add(SW3_ETH1);
-
-        MultiPointToSinglePointIntent intent =
-                new MultiPointToSinglePointIntent(APPID,
-                        selectorBuilder.build(), treatmentBuilder.build(),
-                        ingressPoints, SW1_ETH1);
-
-        return intent;
-    }
-
-    /**
-     * Sets ribTable Field in Router class.
-     *
-     * @throws TestUtilsException
-     */
-    private void setRibTableField(RouteEntry routeEntry)
-            throws TestUtilsException {
-
-        InvertedRadixTree<RouteEntry> ribTable =
-                new ConcurrentInvertedRadixTree<>(
-                new DefaultByteArrayNodeFactory());
-        ribTable.put(RouteEntry.createBinaryString(routeEntry.prefix()),
-                     routeEntry);
-        TestUtils.setField(router, "ribTable4", ribTable);
-    }
-
-    /**
-     * Sets routeIntentsField in IntentSynchronizer class.
-     *
-     * @throws TestUtilsException
-     */
-    private void setRouteIntentsField(RouteEntry routeEntry,
-            MultiPointToSinglePointIntent intent)
-            throws TestUtilsException {
-
-        ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent>
-            routeIntents =  new ConcurrentHashMap<>();
-        routeIntents.put(routeEntry.prefix(), intent);
-        TestUtils.setField(intentSynchronizer, "routeIntents", routeIntents);
-    }
-}
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java
deleted file mode 100644
index a7b4cce..0000000
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * Copyright 2014 Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.sdnip;
-
-import com.google.common.collect.Sets;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.junit.TestUtils;
-import org.onlab.junit.TestUtils.TestUtilsException;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultHost;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostListener;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.intent.AbstractIntentTest;
-import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentOperations;
-import org.onosproject.net.intent.IntentService;
-import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.provider.ProviderId;
-import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
-import org.onosproject.sdnip.config.BgpPeer;
-import org.onosproject.sdnip.config.Interface;
-import org.onosproject.sdnip.config.SdnIpConfigurationService;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import static org.easymock.EasyMock.*;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-/**
- * This class tests adding a route, updating a route, deleting a route,
- * and adding a route whose next hop is the local BGP speaker.
- * <p/>
- * ARP module answers the MAC address synchronously.
- */
-public class RouterTest extends AbstractIntentTest {
-
-    private SdnIpConfigurationService sdnIpConfigService;
-    private InterfaceService interfaceService;
-    private IntentService intentService;
-    private HostService hostService;
-
-    private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
-            DeviceId.deviceId("of:0000000000000001"),
-            PortNumber.portNumber(1));
-
-    private static final ConnectPoint SW2_ETH1 = new ConnectPoint(
-            DeviceId.deviceId("of:0000000000000002"),
-            PortNumber.portNumber(1));
-
-    private static final ConnectPoint SW3_ETH1 = new ConnectPoint(
-            DeviceId.deviceId("of:0000000000000003"),
-            PortNumber.portNumber(1));
-
-    private static final ConnectPoint SW4_ETH1 = new ConnectPoint(
-            DeviceId.deviceId("of:0000000000000004"),
-            PortNumber.portNumber(1));
-
-    private static final ApplicationId APPID = new ApplicationId() {
-        @Override
-        public short id() {
-            return 1;
-        }
-
-        @Override
-        public String name() {
-            return "SDNIP";
-        }
-    };
-
-    private IntentSynchronizer intentSynchronizer;
-    private Router router;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-
-        setUpBgpPeers();
-
-        setUpInterfaceService();
-        setUpHostService();
-
-        intentService = createMock(IntentService.class);
-
-        intentSynchronizer = new IntentSynchronizer(APPID, intentService,
-                                                    sdnIpConfigService,
-                                                    interfaceService);
-        router = new Router(intentSynchronizer, hostService);
-    }
-
-    /**
-     * Sets up BGP peers in external networks.
-     */
-    private void setUpBgpPeers() {
-
-        Map<IpAddress, BgpPeer> peers = new HashMap<>();
-
-        String peerSw1Eth1 = "192.168.10.1";
-        peers.put(IpAddress.valueOf(peerSw1Eth1),
-                new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
-
-        // Two BGP peers are connected to switch 2 port 1.
-        String peer1Sw2Eth1 = "192.168.20.1";
-        peers.put(IpAddress.valueOf(peer1Sw2Eth1),
-                new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
-
-        String peer2Sw2Eth1 = "192.168.20.2";
-        peers.put(IpAddress.valueOf(peer2Sw2Eth1),
-                new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
-
-        String peer1Sw4Eth1 = "192.168.40.1";
-        peers.put(IpAddress.valueOf(peer1Sw4Eth1),
-                new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1));
-
-        sdnIpConfigService = createMock(SdnIpConfigurationService.class);
-        expect(sdnIpConfigService.getBgpPeers()).andReturn(peers).anyTimes();
-        replay(sdnIpConfigService);
-
-    }
-
-    /**
-     * Sets up logical interfaces, which emulate the configured interfaces
-     * in SDN-IP application.
-     */
-    private void setUpInterfaceService() {
-        interfaceService = createMock(InterfaceService.class);
-
-        Set<Interface> interfaces = Sets.newHashSet();
-
-        InterfaceIpAddress ia1 =
-            new InterfaceIpAddress(IpAddress.valueOf("192.168.10.101"),
-                                   IpPrefix.valueOf("192.168.10.0/24"));
-        Interface sw1Eth1 = new Interface(SW1_ETH1,
-                Sets.newHashSet(ia1),
-                MacAddress.valueOf("00:00:00:00:00:01"),
-                VlanId.NONE);
-
-        expect(interfaceService.getInterface(SW1_ETH1)).andReturn(sw1Eth1).anyTimes();
-        interfaces.add(sw1Eth1);
-
-        InterfaceIpAddress ia2 =
-            new InterfaceIpAddress(IpAddress.valueOf("192.168.20.101"),
-                                   IpPrefix.valueOf("192.168.20.0/24"));
-        Interface sw2Eth1 = new Interface(SW2_ETH1,
-                Sets.newHashSet(ia2),
-                MacAddress.valueOf("00:00:00:00:00:02"),
-                VlanId.NONE);
-
-        expect(interfaceService.getInterface(SW2_ETH1)).andReturn(sw2Eth1).anyTimes();
-        interfaces.add(sw2Eth1);
-
-        InterfaceIpAddress ia3 =
-            new InterfaceIpAddress(IpAddress.valueOf("192.168.30.101"),
-                                   IpPrefix.valueOf("192.168.30.0/24"));
-        Interface sw3Eth1 = new Interface(SW3_ETH1,
-                Sets.newHashSet(ia3),
-                MacAddress.valueOf("00:00:00:00:00:03"),
-                VlanId.NONE);
-
-        expect(interfaceService.getInterface(SW3_ETH1)).andReturn(sw3Eth1).anyTimes();
-        interfaces.add(sw3Eth1);
-
-        InterfaceIpAddress ia4 =
-                new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"),
-                                       IpPrefix.valueOf("192.168.40.0/24"));
-            Interface sw4Eth1 = new Interface(SW4_ETH1,
-                    Sets.newHashSet(ia4),
-                    MacAddress.valueOf("00:00:00:00:00:04"),
-                    VlanId.vlanId((short) 1));
-
-            expect(interfaceService.getInterface(SW4_ETH1)).andReturn(sw4Eth1).anyTimes();
-            interfaces.add(sw4Eth1);
-
-        expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
-
-        replay(interfaceService);
-    }
-
-    /**
-     * Sets up the host service with details of some hosts.
-     */
-    private void setUpHostService() {
-        hostService = createMock(HostService.class);
-
-        hostService.addListener(anyObject(HostListener.class));
-        expectLastCall().anyTimes();
-
-        IpAddress host1Address = IpAddress.valueOf("192.168.10.1");
-        Host host1 = new DefaultHost(ProviderId.NONE, HostId.NONE,
-                MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE,
-                new HostLocation(SW1_ETH1, 1),
-                Sets.newHashSet(host1Address));
-
-        expect(hostService.getHostsByIp(host1Address))
-                .andReturn(Sets.newHashSet(host1)).anyTimes();
-        hostService.startMonitoringIp(host1Address);
-        expectLastCall().anyTimes();
-
-
-        IpAddress host2Address = IpAddress.valueOf("192.168.20.1");
-        Host host2 = new DefaultHost(ProviderId.NONE, HostId.NONE,
-                MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
-                new HostLocation(SW2_ETH1, 1),
-                Sets.newHashSet(host2Address));
-
-        expect(hostService.getHostsByIp(host2Address))
-                .andReturn(Sets.newHashSet(host2)).anyTimes();
-        hostService.startMonitoringIp(host2Address);
-        expectLastCall().anyTimes();
-
-        // Next hop on a VLAN
-        IpAddress host3Address = IpAddress.valueOf("192.168.40.1");
-        Host host3 = new DefaultHost(ProviderId.NONE, HostId.NONE,
-                MacAddress.valueOf("00:00:00:00:00:03"), VlanId.vlanId((short) 1),
-                new HostLocation(SW4_ETH1, 1),
-                Sets.newHashSet(host3Address));
-
-        expect(hostService.getHostsByIp(host3Address))
-                .andReturn(Sets.newHashSet(host3)).anyTimes();
-        hostService.startMonitoringIp(host3Address);
-        expectLastCall().anyTimes();
-
-
-        replay(hostService);
-    }
-
-    /**
-     * This method tests adding a route entry.
-     */
-    @Test
-    public void testRouteAdd() throws TestUtilsException {
-        // Construct a route entry
-        RouteEntry routeEntry = new RouteEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"),
-                Ip4Address.valueOf("192.168.10.1"));
-
-        // Construct a MultiPointToSinglePointIntent intent
-        TrafficSelector.Builder selectorBuilder =
-                DefaultTrafficSelector.builder();
-        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
-                routeEntry.prefix());
-
-        TrafficTreatment.Builder treatmentBuilder =
-                DefaultTrafficTreatment.builder();
-        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
-
-        Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
-        ingressPoints.add(SW2_ETH1);
-        ingressPoints.add(SW3_ETH1);
-        ingressPoints.add(SW4_ETH1);
-
-        MultiPointToSinglePointIntent intent =
-                new MultiPointToSinglePointIntent(APPID,
-                        selectorBuilder.build(), treatmentBuilder.build(),
-                        ingressPoints, SW1_ETH1);
-
-        // Set up test expectation
-        reset(intentService);
-        // Setup the expected intents
-        IntentOperations.Builder builder = IntentOperations.builder(APPID);
-        builder.addSubmitOperation(intent);
-        intentService.execute(TestIntentServiceHelper.eqExceptId(
-                                builder.build()));
-        replay(intentService);
-
-        // Call the processRouteUpdates() method in Router class
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
-                                                  routeEntry);
-        router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
-
-        // Verify
-        assertEquals(router.getRoutes4().size(), 1);
-        assertTrue(router.getRoutes4().contains(routeEntry));
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
-        Intent firstIntent =
-            intentSynchronizer.getRouteIntents().iterator().next();
-        IntentKey firstIntentKey = new IntentKey(firstIntent);
-        IntentKey intentKey = new IntentKey(intent);
-        assertTrue(firstIntentKey.equals(intentKey));
-        verify(intentService);
-    }
-
-    /**
-     * This method tests adding a route entry.
-     */
-    @Test
-    public void testRouteAddWithVlan() throws TestUtilsException {
-        // Construct a route entry
-        RouteEntry routeEntry = new RouteEntry(
-                Ip4Prefix.valueOf("3.3.3.0/24"),
-                Ip4Address.valueOf("192.168.40.1"));
-
-        // Construct a MultiPointToSinglePointIntent intent
-        TrafficSelector.Builder selectorBuilder =
-                DefaultTrafficSelector.builder();
-        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                       .matchIPDst(routeEntry.prefix())
-                       .matchVlanId(VlanId.ANY);
-
-        TrafficTreatment.Builder treatmentBuilder =
-                DefaultTrafficTreatment.builder();
-        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:03"))
-                        .setVlanId(VlanId.vlanId((short) 1));
-
-        Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
-        ingressPoints.add(SW1_ETH1);
-        ingressPoints.add(SW2_ETH1);
-        ingressPoints.add(SW3_ETH1);
-
-        MultiPointToSinglePointIntent intent =
-                new MultiPointToSinglePointIntent(APPID,
-                        selectorBuilder.build(), treatmentBuilder.build(),
-                        ingressPoints, SW4_ETH1);
-
-        // Set up test expectation
-        reset(intentService);
-        // Setup the expected intents
-        IntentOperations.Builder builder = IntentOperations.builder(APPID);
-        builder.addSubmitOperation(intent);
-        intentService.execute(TestIntentServiceHelper.eqExceptId(
-                                builder.build()));
-        replay(intentService);
-
-        // Call the processRouteUpdates() method in Router class
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
-                                                  routeEntry);
-        router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
-
-        // Verify
-        assertEquals(router.getRoutes4().size(), 1);
-        assertTrue(router.getRoutes4().contains(routeEntry));
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
-        Intent firstIntent =
-            intentSynchronizer.getRouteIntents().iterator().next();
-        IntentKey firstIntentKey = new IntentKey(firstIntent);
-        IntentKey intentKey = new IntentKey(intent);
-        assertTrue(firstIntentKey.equals(intentKey));
-        verify(intentService);
-    }
-
-    /**
-     * This method tests updating a route entry.
-     *
-     * @throws TestUtilsException
-     */
-    @Test
-    public void testRouteUpdate() throws TestUtilsException {
-        // Firstly add a route
-        testRouteAdd();
-
-        Intent addedIntent =
-            intentSynchronizer.getRouteIntents().iterator().next();
-
-        // Start to construct a new route entry and new intent
-        RouteEntry routeEntryUpdate = new RouteEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"),
-                Ip4Address.valueOf("192.168.20.1"));
-
-        // Construct a new MultiPointToSinglePointIntent intent
-        TrafficSelector.Builder selectorBuilderNew =
-                DefaultTrafficSelector.builder();
-        selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
-                routeEntryUpdate.prefix());
-
-        TrafficTreatment.Builder treatmentBuilderNew =
-                DefaultTrafficTreatment.builder();
-        treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
-
-
-        Set<ConnectPoint> ingressPointsNew = new HashSet<ConnectPoint>();
-        ingressPointsNew.add(SW1_ETH1);
-        ingressPointsNew.add(SW3_ETH1);
-        ingressPointsNew.add(SW4_ETH1);
-
-        MultiPointToSinglePointIntent intentNew =
-                new MultiPointToSinglePointIntent(APPID,
-                        selectorBuilderNew.build(),
-                        treatmentBuilderNew.build(),
-                        ingressPointsNew, SW2_ETH1);
-
-        // Set up test expectation
-        reset(intentService);
-        // Setup the expected intents
-        IntentOperations.Builder builder = IntentOperations.builder(APPID);
-        builder.addWithdrawOperation(addedIntent.id());
-        intentService.execute(TestIntentServiceHelper.eqExceptId(
-                                builder.build()));
-        builder = IntentOperations.builder(APPID);
-        builder.addSubmitOperation(intentNew);
-        intentService.execute(TestIntentServiceHelper.eqExceptId(
-                                builder.build()));
-        replay(intentService);
-
-        // Call the processRouteUpdates() method in Router class
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
-                                                  routeEntryUpdate);
-        router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
-
-        // Verify
-        assertEquals(router.getRoutes4().size(), 1);
-        assertTrue(router.getRoutes4().contains(routeEntryUpdate));
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
-        Intent firstIntent =
-            intentSynchronizer.getRouteIntents().iterator().next();
-        IntentKey firstIntentKey = new IntentKey(firstIntent);
-        IntentKey intentNewKey = new IntentKey(intentNew);
-        assertTrue(firstIntentKey.equals(intentNewKey));
-        verify(intentService);
-    }
-
-    /**
-     * This method tests deleting a route entry.
-     */
-    @Test
-    public void testRouteDelete() throws TestUtilsException {
-        // Firstly add a route
-        testRouteAdd();
-
-        Intent addedIntent =
-            intentSynchronizer.getRouteIntents().iterator().next();
-
-        // Construct the existing route entry
-        RouteEntry routeEntry = new RouteEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"),
-                Ip4Address.valueOf("192.168.10.1"));
-
-        // Set up expectation
-        reset(intentService);
-        // Setup the expected intents
-        IntentOperations.Builder builder = IntentOperations.builder(APPID);
-        builder.addWithdrawOperation(addedIntent.id());
-        intentService.execute(TestIntentServiceHelper.eqExceptId(
-                                builder.build()));
-        replay(intentService);
-
-        // Call the processRouteUpdates() method in Router class
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.DELETE,
-                                                  routeEntry);
-        router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
-
-        // Verify
-        assertEquals(router.getRoutes4().size(), 0);
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
-        verify(intentService);
-    }
-
-    /**
-     * This method tests when the next hop of a route is the local BGP speaker.
-     *
-     * @throws TestUtilsException
-     */
-    @Test
-    public void testLocalRouteAdd() throws TestUtilsException {
-        // Construct a route entry, the next hop is the local BGP speaker
-        RouteEntry routeEntry = new RouteEntry(
-                Ip4Prefix.valueOf("1.1.1.0/24"),
-                Ip4Address.valueOf("0.0.0.0"));
-
-        // Reset intentService to check whether the submit method is called
-        reset(intentService);
-        replay(intentService);
-
-        // Call the processRouteUpdates() method in Router class
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
-                                                  routeEntry);
-        router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
-
-        // Verify
-        assertEquals(router.getRoutes4().size(), 1);
-        assertTrue(router.getRoutes4().contains(routeEntry));
-        assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
-        verify(intentService);
-    }
-}
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java
deleted file mode 100644
index 157c73f..0000000
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
- * Copyright 2014 Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.sdnip;
-
-import com.google.common.collect.Sets;
-import org.easymock.IAnswer;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.onlab.junit.IntegrationTest;
-import org.onlab.junit.TestUtils;
-import org.onlab.junit.TestUtils.TestUtilsException;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.intent.AbstractIntentTest;
-import org.onosproject.net.intent.IntentService;
-import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.sdnip.config.BgpPeer;
-import org.onosproject.sdnip.config.Interface;
-import org.onosproject.sdnip.config.SdnIpConfigurationService;
-
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import static org.easymock.EasyMock.*;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-/**
- * Integration tests for the SDN-IP application.
- * <p/>
- * The tests are very coarse-grained. They feed route updates in to
- * {@link Router} (simulating routes learnt from iBGP module inside SDN-IP
- * application), then they check that the correct intents are created and
- * submitted to the intent service. The entire route processing logic of
- * Router class is tested.
- */
-@Category(IntegrationTest.class)
-public class SdnIpTest extends AbstractIntentTest {
-    private static final int MAC_ADDRESS_LENGTH = 6;
-    private static final int MIN_PREFIX_LENGTH = 1;
-    private static final int MAX_PREFIX_LENGTH = 32;
-
-    private IntentSynchronizer intentSynchronizer;
-    static Router router;
-
-    private SdnIpConfigurationService sdnIpConfigService;
-    private InterfaceService interfaceService;
-    private HostService hostService;
-    private IntentService intentService;
-
-    private Map<IpAddress, BgpPeer> bgpPeers;
-
-    private Random random;
-
-    static final ConnectPoint SW1_ETH1 = new ConnectPoint(
-            DeviceId.deviceId("of:0000000000000001"),
-            PortNumber.portNumber(1));
-
-    static final ConnectPoint SW2_ETH1 = new ConnectPoint(
-            DeviceId.deviceId("of:0000000000000002"),
-            PortNumber.portNumber(1));
-
-    static final ConnectPoint SW3_ETH1 = new ConnectPoint(
-            DeviceId.deviceId("of:0000000000000003"),
-            PortNumber.portNumber(1));
-
-    private static final ApplicationId APPID = new ApplicationId() {
-        @Override
-        public short id() {
-            return 1;
-        }
-
-        @Override
-        public String name() {
-            return "SDNIP";
-        }
-    };
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-
-        setUpInterfaceService();
-        setUpSdnIpConfigService();
-
-        hostService = new TestHostService();
-        intentService = createMock(IntentService.class);
-        random = new Random();
-
-        intentSynchronizer = new IntentSynchronizer(APPID, intentService,
-                                                    sdnIpConfigService,
-                                                    interfaceService);
-        router = new Router(intentSynchronizer, hostService);
-    }
-
-    /**
-     * Sets up InterfaceService and virtual {@link Interface}s.
-     */
-    private void setUpInterfaceService() {
-
-        interfaceService = createMock(InterfaceService.class);
-
-        Set<Interface> interfaces = Sets.newHashSet();
-
-        Set<InterfaceIpAddress> interfaceIpAddresses1 = Sets.newHashSet();
-        interfaceIpAddresses1.add(new InterfaceIpAddress(
-                IpAddress.valueOf("192.168.10.101"),
-                IpPrefix.valueOf("192.168.10.0/24")));
-        Interface sw1Eth1 = new Interface(SW1_ETH1,
-                interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"),
-                VlanId.NONE);
-        interfaces.add(sw1Eth1);
-
-        Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet();
-        interfaceIpAddresses2.add(new InterfaceIpAddress(
-                IpAddress.valueOf("192.168.20.101"),
-                IpPrefix.valueOf("192.168.20.0/24")));
-        Interface sw2Eth1 = new Interface(SW2_ETH1,
-                interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"),
-                VlanId.NONE);
-        interfaces.add(sw2Eth1);
-
-        Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet();
-        interfaceIpAddresses3.add(new InterfaceIpAddress(
-                IpAddress.valueOf("192.168.30.101"),
-                IpPrefix.valueOf("192.168.30.0/24")));
-        Interface sw3Eth1 = new Interface(SW3_ETH1,
-                interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"),
-                VlanId.NONE);
-        interfaces.add(sw3Eth1);
-
-        expect(interfaceService.getInterface(SW1_ETH1)).andReturn(
-                sw1Eth1).anyTimes();
-        expect(interfaceService.getInterface(SW2_ETH1)).andReturn(
-                sw2Eth1).anyTimes();
-        expect(interfaceService.getInterface(SW3_ETH1)).andReturn(
-                sw3Eth1).anyTimes();
-
-        expect(interfaceService.getInterfaces()).andReturn(
-                interfaces).anyTimes();
-        replay(interfaceService);
-    }
-
-    /**
-     * Sets up SdnIpConfigService and BGP peers in external networks.
-     */
-    private void setUpSdnIpConfigService() {
-
-        sdnIpConfigService = createMock(SdnIpConfigurationService.class);
-
-        bgpPeers = new HashMap<>();
-
-        String peerSw1Eth1 = "192.168.10.1";
-        bgpPeers.put(IpAddress.valueOf(peerSw1Eth1),
-                new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
-
-        String peer1Sw2Eth1 = "192.168.20.1";
-        bgpPeers.put(IpAddress.valueOf(peer1Sw2Eth1),
-                new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
-
-        String peer2Sw2Eth1 = "192.168.30.1";
-        bgpPeers.put(IpAddress.valueOf(peer2Sw2Eth1),
-                new BgpPeer("00:00:00:00:00:00:00:03", 1, peer2Sw2Eth1));
-
-        expect(sdnIpConfigService.getBgpPeers()).andReturn(bgpPeers).anyTimes();
-        replay(sdnIpConfigService);
-    }
-
-    /**
-     * Tests adding a set of routes into {@link Router}.
-     * <p/>
-     * Random routes are generated and fed in to the route processing
-     * logic (via processRouteAdd in Router class). We check that the correct
-     * intents are generated and submitted to our mock intent service.
-     *
-     * @throws InterruptedException if interrupted while waiting on a latch
-     * @throws TestUtilsException if exceptions when using TestUtils
-     */
-    @Test
-    public void testAddRoutes() throws InterruptedException, TestUtilsException {
-        int numRoutes = 100;
-
-        final CountDownLatch latch = new CountDownLatch(numRoutes);
-
-        List<RouteUpdate> routeUpdates = generateRouteUpdates(numRoutes);
-
-        // Set up expectation
-        reset(intentService);
-
-        for (RouteUpdate update : routeUpdates) {
-            IpAddress nextHopAddress = update.routeEntry().nextHop();
-
-            // Find out the egress ConnectPoint
-            ConnectPoint egressConnectPoint = getConnectPoint(nextHopAddress);
-
-            MultiPointToSinglePointIntent intent = getIntentForUpdate(update,
-                    generateMacAddress(nextHopAddress),
-                    egressConnectPoint);
-            intentService.submit(TestIntentServiceHelper.eqExceptId(intent));
-
-            expectLastCall().andAnswer(new IAnswer<Object>() {
-                @Override
-                public Object answer() throws Throwable {
-                    latch.countDown();
-                    return null;
-                }
-            }).once();
-        }
-
-        replay(intentService);
-
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-
-        // Add route updates
-        router.processRouteUpdates(routeUpdates);
-
-        latch.await(5000, TimeUnit.MILLISECONDS);
-
-        assertEquals(router.getRoutes4().size(), numRoutes);
-        assertEquals(intentSynchronizer.getRouteIntents().size(),
-                     numRoutes);
-
-        verify(intentService);
-    }
-
-    /**
-     * Tests adding then deleting a set of routes from {@link Router}.
-     * <p/>
-     * Random routes are generated and fed in to the route processing
-     * logic (via processRouteAdd in Router class), and we check that the
-     * correct intents are generated. We then delete the entire set of routes
-     * (by feeding updates to processRouteDelete), and check that the correct
-     * intents are withdrawn from the intent service.
-     *
-     * @throws InterruptedException if interrupted while waiting on a latch
-     * @throws TestUtilsException exceptions when using TestUtils
-     */
-    @Test
-    public void testDeleteRoutes() throws InterruptedException, TestUtilsException {
-        int numRoutes = 100;
-        List<RouteUpdate> routeUpdates = generateRouteUpdates(numRoutes);
-
-        final CountDownLatch installCount = new CountDownLatch(numRoutes);
-        final CountDownLatch deleteCount = new CountDownLatch(numRoutes);
-
-        // Set up expectation
-        reset(intentService);
-
-        for (RouteUpdate update : routeUpdates) {
-            IpAddress nextHopAddress = update.routeEntry().nextHop();
-
-            // Find out the egress ConnectPoint
-            ConnectPoint egressConnectPoint = getConnectPoint(nextHopAddress);
-            MultiPointToSinglePointIntent intent = getIntentForUpdate(update,
-                    generateMacAddress(nextHopAddress),
-                    egressConnectPoint);
-            intentService.submit(TestIntentServiceHelper.eqExceptId(intent));
-            expectLastCall().andAnswer(new IAnswer<Object>() {
-                @Override
-                public Object answer() throws Throwable {
-                    installCount.countDown();
-                    return null;
-                }
-            }).once();
-            intentService.withdraw(TestIntentServiceHelper.eqExceptId(intent));
-            expectLastCall().andAnswer(new IAnswer<Object>() {
-                @Override
-                public Object answer() throws Throwable {
-                    deleteCount.countDown();
-                    return null;
-                }
-            }).once();
-        }
-
-        replay(intentService);
-
-        intentSynchronizer.leaderChanged(true);
-        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-
-        // Send the add updates first
-        router.processRouteUpdates(routeUpdates);
-
-        // Give some time to let the intents be submitted
-        installCount.await(5000, TimeUnit.MILLISECONDS);
-
-        // Send the DELETE updates
-        List<RouteUpdate> deleteRouteUpdates = new ArrayList<>();
-        for (RouteUpdate update : routeUpdates) {
-            RouteUpdate deleteUpdate = new RouteUpdate(RouteUpdate.Type.DELETE,
-                                                       update.routeEntry());
-            deleteRouteUpdates.add(deleteUpdate);
-        }
-        router.processRouteUpdates(deleteRouteUpdates);
-
-        deleteCount.await(5000, TimeUnit.MILLISECONDS);
-
-        assertEquals(0, router.getRoutes4().size());
-        assertEquals(0, intentSynchronizer.getRouteIntents().size());
-        verify(intentService);
-    }
-
-    /**
-     * This methods generates random route updates.
-     *
-     * @param numRoutes the number of route updates to generate
-     * @return a list of route update
-     */
-    private List<RouteUpdate> generateRouteUpdates(int numRoutes) {
-        List<RouteUpdate> routeUpdates = new ArrayList<>(numRoutes);
-
-        Set<Ip4Prefix> prefixes = new HashSet<>();
-
-        for (int i = 0; i < numRoutes; i++) {
-            Ip4Prefix prefix;
-            do {
-                // Generate a random prefix length between MIN_PREFIX_LENGTH
-                // and MAX_PREFIX_LENGTH
-                int prefixLength = random.nextInt(
-                        (MAX_PREFIX_LENGTH - MIN_PREFIX_LENGTH) + 1)
-                        + MIN_PREFIX_LENGTH;
-                prefix =
-                    Ip4Prefix.valueOf(Ip4Address.valueOf(random.nextInt()),
-                                      prefixLength);
-                // We have to ensure we don't generate the same prefix twice
-                // (this is quite easy to happen with small prefix lengths).
-            } while (prefixes.contains(prefix));
-
-            prefixes.add(prefix);
-
-            // Randomly select a peer to use as the next hop
-            BgpPeer nextHop = null;
-            int peerNumber = random.nextInt(sdnIpConfigService.getBgpPeers()
-                    .size());
-            int j = 0;
-            for (BgpPeer peer : sdnIpConfigService.getBgpPeers().values()) {
-                if (j++ == peerNumber) {
-                    nextHop = peer;
-                    break;
-                }
-            }
-
-            assertNotNull(nextHop);
-
-            RouteUpdate update =
-                new RouteUpdate(RouteUpdate.Type.UPDATE,
-                        new RouteEntry(prefix,
-                                       nextHop.ipAddress().getIp4Address()));
-
-            routeUpdates.add(update);
-        }
-
-        return routeUpdates;
-    }
-
-    /**
-     * Generates the MultiPointToSinglePointIntent that should be
-     * submitted/withdrawn for a particular RouteUpdate.
-     *
-     * @param update the RouteUpdate to generate an intent for
-     * @param nextHopMac a MAC address to use as the dst-mac for the intent
-     * @param egressConnectPoint the outgoing ConnectPoint for the intent
-     * @return the generated intent
-     */
-    private MultiPointToSinglePointIntent getIntentForUpdate(RouteUpdate update,
-            MacAddress nextHopMac, ConnectPoint egressConnectPoint) {
-        IpPrefix ip4Prefix = update.routeEntry().prefix();
-
-        TrafficSelector.Builder selectorBuilder =
-                DefaultTrafficSelector.builder();
-
-        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ip4Prefix);
-
-        TrafficTreatment.Builder treatmentBuilder =
-                DefaultTrafficTreatment.builder();
-        treatmentBuilder.setEthDst(nextHopMac);
-
-        Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
-        for (Interface intf : interfaceService.getInterfaces()) {
-            if (!intf.connectPoint().equals(egressConnectPoint)) {
-                ConnectPoint srcPort = intf.connectPoint();
-                ingressPoints.add(srcPort);
-            }
-        }
-
-        MultiPointToSinglePointIntent intent =
-                new MultiPointToSinglePointIntent(APPID,
-                selectorBuilder.build(), treatmentBuilder.build(),
-                ingressPoints, egressConnectPoint);
-
-        return intent;
-    }
-
-    /**
-     * Generates a MAC address based on an IP address.
-     * For the test we need MAC addresses but the actual values don't have any
-     * meaning, so we'll just generate them based on the IP address. This means
-     * we have a deterministic mapping from IP address to MAC address.
-     *
-     * @param ipAddress IP address used to generate a MAC address
-     * @return generated MAC address
-     */
-    static MacAddress generateMacAddress(IpAddress ipAddress) {
-        byte[] macAddress = new byte[MAC_ADDRESS_LENGTH];
-        ByteBuffer bb = ByteBuffer.wrap(macAddress);
-
-        // Put the IP address bytes into the lower four bytes of the MAC
-        // address. Leave the first two bytes set to 0.
-        bb.position(2);
-        bb.put(ipAddress.toOctets());
-
-        return MacAddress.valueOf(bb.array());
-    }
-
-    /**
-     * Finds out the ConnectPoint for a BGP peer address.
-     *
-     * @param bgpPeerAddress the BGP peer address.
-     */
-    private ConnectPoint getConnectPoint(IpAddress bgpPeerAddress) {
-        ConnectPoint connectPoint = null;
-
-        for (BgpPeer bgpPeer: bgpPeers.values()) {
-            if (bgpPeer.ipAddress().equals(bgpPeerAddress)) {
-                connectPoint = bgpPeer.connectPoint();
-                break;
-            }
-        }
-        return connectPoint;
-    }
-}
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/TestHostService.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/TestHostService.java
deleted file mode 100644
index 7e84f8e..0000000
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/TestHostService.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright 2014 Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.sdnip;
-
-import java.util.HashSet;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultHost;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostListener;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.PortAddresses;
-import org.onosproject.net.provider.ProviderId;
-import org.onosproject.sdnip.Router.InternalHostListener;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-
-import com.google.common.collect.Sets;
-
-/**
- * Test version of the HostService which is used to simulate delays in
- * receiving ARP replies, as you would see in a real system due to the time
- * it takes to proxy ARP packets to/from the host. Requests are asynchronous,
- * and replies may come back to the requestor in a different order than the
- * requests were sent, which again you would expect to see in a real system.
- */
-public class TestHostService implements HostService {
-
-    /**
-     * The maximum possible delay before an ARP reply is received.
-     */
-    private static final int MAX_ARP_REPLY_DELAY = 30; // milliseconds
-
-    /**
-     * The probability that we already have the MAC address cached when the
-     * caller calls {@link #getHostsByIp(IpAddress ipAddress)}.
-     */
-    private static final float MAC_ALREADY_KNOWN_PROBABILITY = 0.3f;
-
-    private final ScheduledExecutorService replyTaskExecutor;
-    private final Random random;
-
-    /**
-     * Class constructor.
-     */
-    public TestHostService() {
-        replyTaskExecutor = Executors.newSingleThreadScheduledExecutor();
-        random = new Random();
-    }
-
-    /**
-     * Task used to reply to ARP requests from a different thread. Replies
-     * usually come on a different thread in the real system, so we need to
-     * ensure we test this behavior.
-     */
-    private class ReplyTask implements Runnable {
-        private HostListener listener;
-        private IpAddress ipAddress;
-
-        /**
-         * Class constructor.
-         *
-         * @param listener the client who requests and waits the MAC address
-         * @param ipAddress the target IP address of the request
-         */
-        public ReplyTask(InternalHostListener listener,
-                IpAddress ipAddress) {
-            this.listener = listener;
-            this.ipAddress = ipAddress;
-        }
-
-        @Override
-        public void run() {
-            Host host = getHostsByIp(ipAddress).iterator().next();
-            HostEvent hostevent =
-                    new HostEvent(HostEvent.Type.HOST_ADDED, host);
-            listener.event(hostevent);
-        }
-    }
-
-    @Override
-    public Set<Host> getHostsByIp(IpAddress ipAddress) {
-        float replyChance = random.nextFloat();
-
-        // We don't care what the attachment point is in the test,
-        // so for all the hosts, we use a same ConnectPoint.
-        Host host = new DefaultHost(ProviderId.NONE, HostId.NONE,
-                SdnIpTest.generateMacAddress(ipAddress), VlanId.NONE,
-                new HostLocation(SdnIpTest.SW1_ETH1, 1),
-                Sets.newHashSet(ipAddress));
-
-        if (replyChance < MAC_ALREADY_KNOWN_PROBABILITY) {
-            // Some percentage of the time we already know the MAC address, so
-            // we reply directly when the requestor asks for the MAC address
-            return Sets.newHashSet(host);
-        }
-        return new HashSet<Host>();
-    }
-
-    @Override
-    public void startMonitoringIp(IpAddress ipAddress) {
-
-        // Randomly select an amount of time to delay the reply coming back to
-        int delay = random.nextInt(MAX_ARP_REPLY_DELAY);
-        ReplyTask replyTask = new ReplyTask(
-                (SdnIpTest.router.new InternalHostListener()), ipAddress);
-        replyTaskExecutor.schedule(replyTask, delay, TimeUnit.MILLISECONDS);
-    }
-
-    @Override
-    public int getHostCount() {
-        return 0;
-    }
-
-    @Override
-    public Iterable<Host> getHosts() {
-        return null;
-    }
-
-    @Override
-    public Host getHost(HostId hostId) {
-        return null;
-    }
-
-    @Override
-    public Set<Host> getHostsByVlan(VlanId vlanId) {
-        return null;
-    }
-
-    @Override
-    public Set<Host> getHostsByMac(MacAddress mac) {
-        return null;
-    }
-
-    @Override
-    public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
-        return null;
-    }
-
-    @Override
-    public Set<Host> getConnectedHosts(DeviceId deviceId) {
-        return null;
-    }
-
-    @Override
-    public void stopMonitoringIp(IpAddress ip) {
-
-    }
-
-    @Override
-    public void requestMac(IpAddress ip) {
-
-    }
-
-    @Override
-    public Set<PortAddresses> getAddressBindings() {
-        return null;
-    }
-
-    @Override
-    public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
-        return null;
-    }
-
-    @Override
-    public void addListener(HostListener listener) {
-
-    }
-
-    @Override
-    public void removeListener(HostListener listener) {
-
-    }
-
-}