[Falcon] Componentize IntentSynchronizer and SdnIpFib.

Change-Id: Ic384ce00572ae1e4bbf94b4de814cea3499d3828
diff --git a/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
index 96aa06e..ffbdf10 100644
--- a/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
+++ b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
@@ -47,9 +47,9 @@
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.routing.IntentRequestListener;
+import org.onosproject.routing.IntentSynchronizationService;
 import org.onosproject.routing.RouteEntry;
 import org.onosproject.routing.RoutingService;
-import org.onosproject.routing.SdnIpService;
 import org.onosproject.routing.config.RoutingConfigurationService;
 import org.slf4j.Logger;
 
@@ -86,7 +86,7 @@
     protected RoutingService routingService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected SdnIpService sdnIpService;
+    protected IntentSynchronizationService intentSynchronizer;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected RoutingConfigurationService config;
@@ -109,8 +109,7 @@
         appId = coreService.registerApplication(APP_NAME);
 
         intentRequestListener = new ReactiveRoutingFib(appId, hostService,
-                config, interfaceService,
-                sdnIpService.getIntentSynchronizationService());
+                config, interfaceService, intentSynchronizer);
 
         packetService.addProcessor(processor, PacketProcessor.director(2));
         requestIntercepts();
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java b/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationAdminService.java
similarity index 72%
rename from apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java
rename to apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationAdminService.java
index 0945336..14a274f 100644
--- a/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationAdminService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014-2015 Open Networking Laboratory
+ * 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.
@@ -13,12 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.routing;
 
 /**
- * Service interface exported by SDN-IP.
+ * Administrative APIs for managing intent synchronization.
  */
-public interface SdnIpService {
+public interface IntentSynchronizationAdminService {
 
     /**
      * Changes whether this SDN-IP instance is the primary or not based on the
@@ -29,11 +30,7 @@
     void modifyPrimary(boolean isPrimary);
 
     /**
-     * Gets the intent synchronization service.
-     *
-     * @return intent synchronization service
+     * Withdraws all intents.
      */
-    // TODO fix service resolution in SDN-IP
-    IntentSynchronizationService getIntentSynchronizationService();
-
+    void removeIntents();
 }
diff --git a/apps/routing-api/src/test/java/org/onosproject/routing/RoutingServiceAdapter.java b/apps/routing-api/src/test/java/org/onosproject/routing/RoutingServiceAdapter.java
new file mode 100644
index 0000000..6fd52fd
--- /dev/null
+++ b/apps/routing-api/src/test/java/org/onosproject/routing/RoutingServiceAdapter.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import org.onlab.packet.IpAddress;
+
+import java.util.Collection;
+
+/**
+ * Routing service adapter.
+ */
+public class RoutingServiceAdapter implements RoutingService {
+
+    @Override
+    public void start() {
+
+    }
+
+    @Override
+    public void addFibListener(FibListener fibListener) {
+
+    }
+
+    @Override
+    public void stop() {
+
+    }
+
+    @Override
+    public Collection<RouteEntry> getRoutes4() {
+        return null;
+    }
+
+    @Override
+    public Collection<RouteEntry> getRoutes6() {
+        return null;
+    }
+
+    @Override
+    public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) {
+        return null;
+    }
+}
diff --git a/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java b/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java
index 75d789a..dda533a 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java
@@ -35,7 +35,6 @@
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onosproject.core.CoreService;
-import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.Host;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
@@ -107,9 +106,6 @@
     protected BgpService bgpService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected InterfaceService interfaceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected RoutingConfigurationService routingConfigurationService;
 
     private ExecutorService bgpUpdatesExecutor;
@@ -123,7 +119,7 @@
                 new DefaultByteArrayNodeFactory());
 
         routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
-                HashMultimap.<IpAddress, RouteEntry>create());
+                HashMultimap.create());
 
         coreService.registerApplication(ROUTER_APP_ID);
 
diff --git a/apps/sdnip/pom.xml b/apps/sdnip/pom.xml
index 67ae9a3..1151b0e 100644
--- a/apps/sdnip/pom.xml
+++ b/apps/sdnip/pom.xml
@@ -58,7 +58,15 @@
 
         <dependency>
             <groupId>org.onosproject</groupId>
-            <artifactId>onos-app-routing</artifactId>
+            <artifactId>onos-app-routing-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-app-routing-api</artifactId>
+            <scope>test</scope>
+            <classifier>tests</classifier>
             <version>${project.version}</version>
         </dependency>
 
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 221fc99..35383dc 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java
@@ -15,101 +15,117 @@
  */
 package org.onosproject.sdnip;
 
-import static java.util.concurrent.Executors.newSingleThreadExecutor;
-import static org.onlab.util.Tools.groupedThreads;
+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.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipEvent;
+import org.onosproject.cluster.LeadershipEventListener;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.Key;
+import org.onosproject.routing.IntentSynchronizationAdminService;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentService;
-import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.IntentUtils;
-import org.onosproject.net.intent.Key;
-import org.onosproject.routing.IntentSynchronizationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
 
 /**
- * Synchronizes intents between the in-memory intent store and the
- * IntentService.
+ * Synchronizes intents between an in-memory intent store and the IntentService.
  */
-public class IntentSynchronizer implements IntentSynchronizationService {
+@Service
+@Component(immediate = true)
+public class IntentSynchronizer implements IntentSynchronizationService,
+        IntentSynchronizationAdminService {
 
-    private static final Logger log =
-        LoggerFactory.getLogger(IntentSynchronizer.class);
+    private static final Logger log = LoggerFactory.getLogger(IntentSynchronizer.class);
 
-    private final ApplicationId appId;
-    private final IntentService intentService;
+    private static final String APP_NAME = "org.onosproject.intentsynchronizer";
 
-    private final Map<Key, Intent> intents;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
 
-    //
-    // State to deal with the Leader election and pushing Intents
-    //
-    private final ExecutorService intentsSynchronizerExecutor;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentService intentService;
+
+    private ApplicationId appId;
+
+    private final InternalLeadershipListener leadershipEventListener =
+            new InternalLeadershipListener();
+
+    private final Map<Key, Intent> intents = new ConcurrentHashMap<>();
+
+    private ExecutorService intentsSynchronizerExecutor;
+
     private volatile boolean isElectedLeader = false;
     private volatile boolean isActivatedLeader = false;
 
-    /**
-     * Class constructor.
-     *
-     * @param appId the Application ID
-     * @param intentService the intent service
-     */
-    public IntentSynchronizer(ApplicationId appId, IntentService intentService) {
-        this(appId, intentService,
-                newSingleThreadExecutor(groupedThreads("onos/" + appId, "sync")));
+    @Activate
+    public void activate() {
+        intentsSynchronizerExecutor = createExecutor();
+
+        this.appId = coreService.registerApplication(APP_NAME);
+
+        leadershipService.addListener(leadershipEventListener);
+        leadershipService.runForLeadership(appId.name());
+
+        log.info("Started");
     }
 
-    /**
-     * Class constructor.
-     *
-     * @param appId the Application ID
-     * @param intentService the intent service
-     * @param executorService executor service for synchronization thread
-     */
-    public IntentSynchronizer(ApplicationId appId, IntentService intentService,
-                       ExecutorService executorService) {
-        this.appId = appId;
-        this.intentService = intentService;
+    @Deactivate
+    public void deactivate() {
+        leadershipService.withdraw(appId.name());
+        leadershipService.removeListener(leadershipEventListener);
 
-        intents = new ConcurrentHashMap<>();
-
-        intentsSynchronizerExecutor = executorService;
-    }
-
-    /**
-     * Starts the synchronizer.
-     */
-    public void start() {
-
-    }
-
-    /**
-     * Stops the synchronizer.
-     */
-    public void stop() {
         synchronized (this) {
-            // Stop the thread(s)
             intentsSynchronizerExecutor.shutdownNow();
-            log.info("Intents Synchronizer Executor shutdown completed");
-
         }
+
+        log.info("Stopped");
     }
 
     /**
-     * Withdraws all intents.
+     * Creates an executor that will be used for synchronization tasks.
+     * <p>
+     * Can be overridden to change the type of executor used.
+     * </p>
+     *
+     * @return executor service
      */
+    protected ExecutorService createExecutor() {
+        return newSingleThreadExecutor(groupedThreads("onos/" + appId, "sync"));
+    }
+
+    @Override
     public void removeIntents() {
         if (!isElectedLeader) {
-            // only leader will withdraw intents
+            // Only leader will withdraw intents
             return;
         }
 
@@ -152,18 +168,19 @@
      *
      * @param isLeader true if this instance is now the leader, otherwise false
      */
-    public void leaderChanged(boolean isLeader) {
+    private void leaderChanged(boolean isLeader) {
         log.debug("Leader changed: {}", isLeader);
 
         if (!isLeader) {
             this.isElectedLeader = false;
             this.isActivatedLeader = false;
-            return;                     // Nothing to do
+            // Nothing to do
+            return;
         }
         this.isActivatedLeader = false;
         this.isElectedLeader = true;
 
-        // Run the synchronization method off-thread
+        // Run the synchronization task
         intentsSynchronizerExecutor.execute(this::synchronizeIntents);
     }
 
@@ -232,10 +249,49 @@
         }
 
         if (isElectedLeader) {
-            isActivatedLeader = true;       // Allow push of Intents
+            // Allow push of Intents
+            isActivatedLeader = true;
         } else {
             isActivatedLeader = false;
         }
         log.debug("Intent synchronization completed");
     }
+
+    @Override
+    public void modifyPrimary(boolean isPrimary) {
+        leaderChanged(isPrimary);
+    }
+
+    /**
+     * A listener for leadership events.
+     */
+    private class InternalLeadershipListener implements LeadershipEventListener {
+
+        @Override
+        public void event(LeadershipEvent event) {
+            if (!event.subject().topic().equals(appId.name())) {
+                // Not our topic: ignore
+                return;
+            }
+            if (!Objects.equals(event.subject().leader(),
+                    clusterService.getLocalNode().id())) {
+                // The event is not about this instance: ignore
+                return;
+            }
+
+            switch (event.type()) {
+            case LEADER_ELECTED:
+                log.info("IntentSynchronizer gained leadership");
+                leaderChanged(true);
+                break;
+            case LEADER_BOOTED:
+                log.info("IntentSynchronizer lost leadership");
+                leaderChanged(false);
+                break;
+            case LEADER_REELECTED:
+            default:
+                break;
+            }
+        }
+    }
 }
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 ace888d..d0a5c22 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
@@ -15,136 +15,79 @@
  */
 package org.onosproject.sdnip;
 
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Objects;
-
 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.onosproject.app.ApplicationService;
-import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.ControllerNode;
-import org.onosproject.cluster.LeadershipEvent;
-import org.onosproject.cluster.LeadershipEventListener;
-import org.onosproject.cluster.LeadershipService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.intent.IntentService;
+import org.onosproject.routing.IntentSynchronizationAdminService;
 import org.onosproject.routing.IntentSynchronizationService;
 import org.onosproject.routing.RoutingService;
-import org.onosproject.routing.SdnIpService;
-import org.onosproject.routing.config.RoutingConfigurationService;
 import org.slf4j.Logger;
 
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Component for the SDN-IP peering application.
  */
 @Component(immediate = true)
-@Service
-public class SdnIp implements SdnIpService {
+public class SdnIp {
 
-    private static final String SDN_IP_APP = "org.onosproject.sdnip";
+    public static final String SDN_IP_APP = "org.onosproject.sdnip";
     private final Logger log = getLogger(getClass());
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected IntentService intentService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ApplicationService applicationService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ClusterService clusterService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected LeadershipService leadershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected RoutingService routingService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected RoutingConfigurationService config;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigService networkConfigService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected InterfaceService interfaceService;
 
-    private IntentSynchronizer intentSynchronizer;
-    private PeerConnectivityManager peerConnectivity;
-    private SdnIpFib fib;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentSynchronizationService intentSynchronizer;
 
-    private LeadershipEventListener leadershipEventListener =
-            new InnerLeadershipEventListener();
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentSynchronizationAdminService intentSynchronizerAdmin;
+
+    private PeerConnectivityManager peerConnectivity;
+
     private ApplicationId appId;
-    private ControllerNode localControllerNode;
 
     @Activate
     protected void activate() {
-        log.info("SDN-IP started");
-
         appId = coreService.registerApplication(SDN_IP_APP);
 
-        localControllerNode = clusterService.getLocalNode();
-
-        intentSynchronizer = new IntentSynchronizer(appId, intentService);
-        intentSynchronizer.start();
-
         peerConnectivity = new PeerConnectivityManager(appId,
                                                        intentSynchronizer,
                                                        networkConfigService,
-                coreService.getAppId(RoutingService.ROUTER_APP_ID),
+                coreService.registerApplication(RoutingService.ROUTER_APP_ID),
                                                        interfaceService);
         peerConnectivity.start();
 
-        fib = new SdnIpFib(appId, interfaceService, intentSynchronizer);
-
-        routingService.addFibListener(fib);
-        routingService.start();
-
-        leadershipService.addListener(leadershipEventListener);
-        leadershipService.runForLeadership(appId.name());
-
+        // TODO fix removing intents
         applicationService.registerDeactivateHook(appId,
-                intentSynchronizer::removeIntents);
+                intentSynchronizerAdmin::removeIntents);
 
+        log.info("SDN-IP started");
     }
 
     @Deactivate
     protected void deactivate() {
-        routingService.stop();
         peerConnectivity.stop();
-        intentSynchronizer.stop();
-
-        leadershipService.withdraw(appId.name());
-        leadershipService.removeListener(leadershipEventListener);
 
         log.info("SDN-IP Stopped");
     }
 
-    @Override
-    public void modifyPrimary(boolean isPrimary) {
-        intentSynchronizer.leaderChanged(isPrimary);
-    }
-
-    @Override
-    public IntentSynchronizationService getIntentSynchronizationService() {
-        return intentSynchronizer;
-    }
-
     /**
      * Converts DPIDs of the form xx:xx:xx:xx:xx:xx:xx to OpenFlow provider
      * device URIs.
@@ -156,38 +99,5 @@
         return "of:" + dpid.replace(":", "");
     }
 
-    /**
-     * A listener for Leadership Events.
-     */
-    private class InnerLeadershipEventListener
-        implements LeadershipEventListener {
 
-        @Override
-        public void event(LeadershipEvent event) {
-            log.debug("Leadership Event: time = {} type = {} event = {}",
-                      event.time(), event.type(), event);
-
-            if (!event.subject().topic().equals(appId.name())) {
-                return;         // Not our topic: ignore
-            }
-            if (!Objects.equals(event.subject().leader(), localControllerNode.id())) {
-                return;         // The event is not about this instance: ignore
-            }
-
-            switch (event.type()) {
-            case LEADER_ELECTED:
-                log.info("SDN-IP Leader Elected");
-                intentSynchronizer.leaderChanged(true);
-                break;
-            case LEADER_BOOTED:
-                log.info("SDN-IP Leader Lost Election");
-                intentSynchronizer.leaderChanged(false);
-                break;
-            case LEADER_REELECTED:
-                break;
-            default:
-                break;
-            }
-        }
-    }
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
index 9113e01..ac9baab 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
@@ -17,12 +17,18 @@
 package org.onosproject.sdnip;
 
 import com.google.common.collect.ImmutableList;
+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.onlab.packet.Ethernet;
 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.core.CoreService;
 import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
@@ -37,6 +43,7 @@
 import org.onosproject.routing.FibListener;
 import org.onosproject.routing.FibUpdate;
 import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.routing.RoutingService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,40 +58,49 @@
 /**
  * FIB component of SDN-IP.
  */
-public class SdnIpFib implements FibListener {
+@Component(immediate = true)
+public class SdnIpFib {
     private Logger log = LoggerFactory.getLogger(getClass());
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InterfaceService interfaceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentSynchronizationService intentSynchronizer;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RoutingService routingService;
+
+    private final InternalFibListener fibListener = new InternalFibListener();
+
     private static final int PRIORITY_OFFSET = 100;
     private static final int PRIORITY_MULTIPLIER = 5;
     protected static final ImmutableList<Constraint> CONSTRAINTS
             = ImmutableList.of(new PartialFailureConstraint());
 
-    private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
+    private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents
+            = new ConcurrentHashMap<>();
 
-    private final ApplicationId appId;
-    private final InterfaceService interfaceService;
-    private final IntentSynchronizationService intentSynchronizer;
+    private ApplicationId appId;
 
-    /**
-     * Class constructor.
-     *
-     * @param appId application ID to use when generating intents
-     * @param interfaceService interface service
-     * @param intentSynchronizer intent synchronizer
-     */
-    public SdnIpFib(ApplicationId appId, InterfaceService interfaceService,
-                    IntentSynchronizationService intentSynchronizer) {
-        routeIntents = new ConcurrentHashMap<>();
+    @Activate
+    public void activate() {
+        appId = coreService.getAppId(SdnIp.SDN_IP_APP);
 
-        this.appId = appId;
-        this.interfaceService = interfaceService;
-        this.intentSynchronizer = intentSynchronizer;
+        routingService.addFibListener(fibListener);
+        routingService.start();
     }
 
+    @Deactivate
+    public void deactivate() {
+        // TODO remove listener
+        routingService.stop();
+    }
 
-    @Override
-    public void update(Collection<FibUpdate> updates,
-                       Collection<FibUpdate> withdraws) {
+    private void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
         int submitCount = 0, withdrawCount = 0;
         //
         // NOTE: Semantically, we MUST withdraw existing intents before
@@ -224,4 +240,11 @@
                 .build();
     }
 
+    private class InternalFibListener implements FibListener {
+        @Override
+        public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
+            SdnIpFib.this.update(updates, withdraws);
+        }
+    }
+
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java
index 7a17cfe..11da428 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java
@@ -18,10 +18,10 @@
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.routing.SdnIpService;
+import org.onosproject.routing.IntentSynchronizationAdminService;
 
 /**
- * Command to change whether this SDNIP instance is primary or not.
+ * Command to change whether this instance's intent synchronizer is primary.
  */
 @Command(scope = "onos", name = "sdnip-set-primary",
          description = "Changes the primary status of this SDN-IP instance")
@@ -34,7 +34,7 @@
 
     @Override
     protected void execute() {
-        get(SdnIpService.class).modifyPrimary(isPrimary);
+        get(IntentSynchronizationAdminService.class).modifyPrimary(isPrimary);
     }
 
 }
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 1265cc1..37c50d3 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
@@ -29,7 +29,9 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.TestApplicationId;
+import org.onosproject.cluster.LeadershipServiceAdapter;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
 import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
@@ -43,14 +45,15 @@
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.IntentUtils;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.intent.IntentUtils;
 import org.onosproject.routing.RouteEntry;
 
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
@@ -88,7 +91,8 @@
     private IntentSynchronizer intentSynchronizer;
     private final Set<Interface> interfaces = Sets.newHashSet();
 
-    private static final ApplicationId APPID = TestApplicationId.create("SDNIP");
+    private static final ApplicationId APPID =
+            TestApplicationId.create("intent-sync-test");
 
     @Before
     public void setUp() throws Exception {
@@ -98,8 +102,13 @@
 
         intentService = createMock(IntentService.class);
 
-        intentSynchronizer = new IntentSynchronizer(APPID, intentService,
-                MoreExecutors.newDirectExecutorService());
+        intentSynchronizer = new TestIntentSynchronizer();
+
+        intentSynchronizer.coreService = new TestCoreService();
+        intentSynchronizer.leadershipService = new TestLeadershipService();
+        intentSynchronizer.intentService = intentService;
+
+        intentSynchronizer.activate();
     }
 
     /**
@@ -268,7 +277,7 @@
         // Give the leadership to the intent synchronizer. It will now attempt
         // to synchronize the intents in the store with the intents it has
         // recorded based on the earlier user input.
-        intentSynchronizer.leaderChanged(true);
+        intentSynchronizer.modifyPrimary(true);
 
         verify(intentService);
     }
@@ -290,7 +299,7 @@
 
         // Give the intent synchronizer leadership so it will submit intents
         // to the intent service
-        intentSynchronizer.leaderChanged(true);
+        intentSynchronizer.modifyPrimary(true);
 
         // Test the submit
         intentSynchronizer.submit(intent);
@@ -303,7 +312,7 @@
         reset(intentService);
         replay(intentService);
 
-        intentSynchronizer.leaderChanged(false);
+        intentSynchronizer.modifyPrimary(false);
 
         intentSynchronizer.submit(intent);
 
@@ -328,7 +337,7 @@
 
         // Give the intent synchronizer leadership so it will submit intents
         // to the intent service
-        intentSynchronizer.leaderChanged(true);
+        intentSynchronizer.modifyPrimary(true);
 
         // Test the submit then withdraw
         intentSynchronizer.submit(intent);
@@ -342,7 +351,7 @@
         reset(intentService);
         replay(intentService);
 
-        intentSynchronizer.leaderChanged(false);
+        intentSynchronizer.modifyPrimary(false);
 
         intentSynchronizer.submit(intent);
         intentSynchronizer.withdraw(intent);
@@ -418,4 +427,22 @@
                 "ingressPoints", intent.ingressPoints());
         return intentNew;
     }
+
+    private class TestIntentSynchronizer extends IntentSynchronizer {
+        @Override
+        protected ExecutorService createExecutor() {
+            return MoreExecutors.newDirectExecutorService();
+        }
+    }
+
+    private class TestCoreService extends CoreServiceAdapter {
+        @Override
+        public ApplicationId registerApplication(String name) {
+            return APPID;
+        }
+    }
+
+    private class TestLeadershipService extends LeadershipServiceAdapter {
+
+    }
 }
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java
index 5466d52..5c3243f 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java
@@ -28,6 +28,7 @@
 import org.onlab.packet.VlanId;
 import org.onosproject.TestApplicationId;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
 import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
@@ -42,8 +43,10 @@
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
 import org.onosproject.routing.FibEntry;
+import org.onosproject.routing.FibListener;
 import org.onosproject.routing.FibUpdate;
 import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.routing.RoutingServiceAdapter;
 import org.onosproject.routing.config.BgpPeer;
 import org.onosproject.routing.config.RoutingConfigurationService;
 
@@ -90,6 +93,8 @@
 
     private static final ApplicationId APPID = TestApplicationId.create("SDNIP");
 
+    private FibListener fibListener;
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
@@ -106,7 +111,13 @@
 
         intentSynchronizer = createMock(IntentSynchronizationService.class);
 
-        sdnipFib = new SdnIpFib(APPID, interfaceService, intentSynchronizer);
+        sdnipFib = new SdnIpFib();
+        sdnipFib.routingService = new TestRoutingService();
+        sdnipFib.coreService = new TestCoreService();
+        sdnipFib.interfaceService = interfaceService;
+        sdnipFib.intentSynchronizer = intentSynchronizer;
+
+        sdnipFib.activate();
     }
 
     /**
@@ -242,7 +253,7 @@
 
         // Send in the UPDATE FibUpdate
         FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
-        sdnipFib.update(Collections.singleton(fibUpdate), Collections.emptyList());
+        fibListener.update(Collections.singleton(fibUpdate), Collections.emptyList());
 
         verify(intentSynchronizer);
     }
@@ -295,7 +306,7 @@
 
         // Send in the UPDATE FibUpdate
         FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
-        sdnipFib.update(Collections.singleton(fibUpdate), Collections.emptyList());
+        fibListener.update(Collections.singleton(fibUpdate), Collections.emptyList());
 
         verify(intentSynchronizer);
     }
@@ -354,7 +365,7 @@
         // Send in the UPDATE FibUpdate
         FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
                 fibEntryUpdate);
-        sdnipFib.update(Collections.singletonList(fibUpdate),
+        fibListener.update(Collections.singletonList(fibUpdate),
                 Collections.emptyList());
 
         verify(intentSynchronizer);
@@ -410,8 +421,23 @@
 
         // Send in the DELETE FibUpdate
         FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.DELETE, fibEntry);
-        sdnipFib.update(Collections.emptyList(), Collections.singletonList(fibUpdate));
+        fibListener.update(Collections.emptyList(), Collections.singletonList(fibUpdate));
 
         verify(intentSynchronizer);
     }
+
+    private class TestCoreService extends CoreServiceAdapter {
+        @Override
+        public ApplicationId getAppId(String name) {
+            return APPID;
+        }
+    }
+
+    private class TestRoutingService extends RoutingServiceAdapter {
+
+        @Override
+        public void addFibListener(FibListener fibListener) {
+            SdnIpFibTest.this.fibListener = fibListener;
+        }
+    }
 }