ONOS-1871: Send Topology MastershipEvent info to Topology listeners.

The MastershipEvent info is needed by applications such as the GUI
(via the Websocket client).

NOTE: Previously, the Mastership Events were sent only when there
was any change, but no Mastership Events were sent when a listener
has just subscribed.

The solution is to add a mechanism for creating and sending Topology
Snapshot event to new listeners, and that event also includes
the Mastership Events.

The modifications are:

 * Renamed and updated the ITopologyService API for adding/removing
   Topology listeners:
   OLD: registerTopologyListener() and deregisterTopologyListener()
   NEW: addListener() and removeListener()

   Also, addListener() has a second argument:
       "boolean startFromSnapshot"
   If that argument is true, and if the topology is not empty, the first
   (expected) event to that listener should be a snapshot of the current
   topology.

 * Added TopologyEvents() constructor for ADDED events only. Such event
   can be used to represent a snapshot of the topology.

 * Added new APIs to TopologyInternal:
   getAllSwitchEvents(), getAllPortEvents(), get AllLinkEvents(),
   getAllHostEvents()
   Those APIs are needed for creating a snapshot of the topology.

 * Added a mechanism for creating empty (NO-OP) TopologyEvent instance,
   and use that mechanism to "wake-up" the EventHandler processing thread
   when it needs to send Topology Snapshot to a new listener.
   This solution is (kind-of) a hack.

Change-Id: Ie1eb52242f58682aac61f54af29c3b5d291ac0bd
diff --git a/src/main/java/net/onrc/onos/apps/websocket/TopologyWebSocket.java b/src/main/java/net/onrc/onos/apps/websocket/TopologyWebSocket.java
index 1a83b94..9d9ed6f 100644
--- a/src/main/java/net/onrc/onos/apps/websocket/TopologyWebSocket.java
+++ b/src/main/java/net/onrc/onos/apps/websocket/TopologyWebSocket.java
@@ -2,7 +2,6 @@
 
 import net.onrc.onos.core.topology.ITopologyListener;
 import net.onrc.onos.core.topology.ITopologyService;
-import net.onrc.onos.core.topology.Topology;
 import net.onrc.onos.core.topology.TopologyEvents;
 
 import java.io.IOException;
@@ -65,7 +64,7 @@
      */
     private void shutdown() {
         ITopologyService topologyService = WebSocketManager.topologyService;
-        topologyService.deregisterTopologyListener(this);
+        topologyService.removeListener(this);
         this.isOpen = false;            // Stop the thread
     }
 
@@ -124,34 +123,8 @@
         // Initialization and Topology Service registration
         //
         this.socketSession = session;
-        ObjectMapper mapper = new ObjectMapper();
-        String topologyJson = null;
         ITopologyService topologyService = WebSocketManager.topologyService;
-        topologyService.registerTopologyListener(this);
-
-        //
-        // Get the initial topology and encode it in JSON
-        //
-        Topology topology = topologyService.getTopology();
-        topology.acquireReadLock();
-        try {
-            topologyJson = mapper.writeValueAsString(topology);
-        } catch (IOException e) {
-            log.debug("Exception encoding topology as JSON: ", e);
-        } finally {
-            topology.releaseReadLock();
-        }
-
-        //
-        // Send the initial topology
-        //
-        if (topologyJson != null) {
-            try {
-                session.getBasicRemote().sendText(topologyJson);
-            } catch (IOException e) {
-                log.debug("Exception sending TopologyWebSocket topology: ", e);
-            }
-        }
+        topologyService.addListener(this, true);
 
         // Start the thread
         start();
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java b/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
index 12d85d6..ccad3ba 100644
--- a/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModule.java
@@ -525,7 +525,7 @@
         opEventChannel = datagridService.createChannel(
                 INTENT_OP_EVENT_CHANNEL_NAME, Long.class, IntentOperationList.class);
         datagridService.addListener(INTENT_STATE_EVENT_CHANNEL_NAME, this, Long.class, IntentStateList.class);
-        topologyService.registerTopologyListener(this);
+        topologyService.addListener(this, false);
         persistIntent = new PersistIntent(controllerRegistry);
         restApi.addRestletRoutable(new IntentWebRoutable());
     }
diff --git a/src/main/java/net/onrc/onos/core/topology/ITopologyService.java b/src/main/java/net/onrc/onos/core/topology/ITopologyService.java
index b6280bc..7ab5c47 100644
--- a/src/main/java/net/onrc/onos/core/topology/ITopologyService.java
+++ b/src/main/java/net/onrc/onos/core/topology/ITopologyService.java
@@ -14,19 +14,22 @@
     public Topology getTopology();
 
     /**
-     * Registers a listener for topology events.
+     * Adds a listener for topology events.
      *
-     * @param listener the listener to register
+     * @param listener the listener to add.
+     * @param startFromSnapshot if true, and if the topology is not
+     * empty, the first event should be a snapshot of the current topology.
      */
-    public void registerTopologyListener(ITopologyListener listener);
+    public void addListener(ITopologyListener listener,
+                            boolean startFromSnapshot);
 
     /**
-     * Deregisters a listener for topology events. The listener will no longer
+     * Removes a listener for topology events. The listener will no longer
      * receive topology events after this call.
      *
-     * @param listener the listener to deregister
+     * @param listener the listener to remove.
      */
-    public void deregisterTopologyListener(ITopologyListener listener);
+    public void removeListener(ITopologyListener listener);
 
     /**
      * Allows a module to get a reference to the southbound interface to
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyEvent.java b/src/main/java/net/onrc/onos/core/topology/TopologyEvent.java
index c51a157..82fa6e5 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyEvent.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyEvent.java
@@ -40,6 +40,20 @@
     }
 
     /**
+     * Constructor for creating an empty (NO-OP) Topology Event.
+     *
+     * @param onosInstanceId the ONOS Instance ID that originates the event.
+     */
+    protected TopologyEvent(OnosInstanceId onosInstanceId) {
+        mastershipEvent = null;
+        switchEvent = null;
+        portEvent = null;
+        linkEvent = null;
+        hostEvent = null;
+        this.onosInstanceId = checkNotNull(onosInstanceId);
+    }
+
+    /**
      * Constructor for given Switch Mastership event.
      *
      * @param mastershipEvent the Switch Mastership event to use.
@@ -270,8 +284,13 @@
                 eventStr = hostEvent.toString();
                 break stringLabel;
             }
+            // Test whether this is NO-OP event
+            if (onosInstanceId != null) {
+                eventStr = "NO-OP";
+                break stringLabel;
+            }
             // No event found
-            return "[Empty TopologyEvent]";
+            return "[Unknown TopologyEvent]";
         }
 
         return "[TopologyEvent " + eventStr + " from " +
@@ -319,6 +338,12 @@
                 eventId = hostEvent.getIDasByteBuffer();
                 break idLabel;
             }
+            // Test whether this is NO-OP event
+            if (onosInstanceId != null) {
+                String id = "NO-OP";
+                eventId = ByteBuffer.wrap(id.getBytes(StandardCharsets.UTF_8));
+                break idLabel;
+            }
             // No event found
             throw new IllegalStateException("Invalid TopologyEvent ID");
         }
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyEventPreprocessor.java b/src/main/java/net/onrc/onos/core/topology/TopologyEventPreprocessor.java
index 69d9b07..475dca1 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyEventPreprocessor.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyEventPreprocessor.java
@@ -217,6 +217,11 @@
         for (EventEntry<TopologyEvent> event : events) {
             List<EventEntry<TopologyEvent>> postponedEvents = null;
 
+            // Ignore NO-OP events
+            if (event.isNoop()) {
+                continue;
+            }
+
             TopologyEvent topologyEvent = event.eventData();
             OnosInstanceId onosInstanceId = topologyEvent.getOnosInstanceId();
 
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyEvents.java b/src/main/java/net/onrc/onos/core/topology/TopologyEvents.java
index 543c1cb..c930714 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyEvents.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyEvents.java
@@ -42,10 +42,11 @@
     private final Collection<HostEvent> removedHostEvents;
 
     /**
-     * Constructor.
+     * Constructor for added and removed events.
      *
      * @param addedMastershipEvents the collection of added Mastership Events.
-     * @param removedMastershipEvents the collection of removed Mastership Events.
+     * @param removedMastershipEvents the collection of removed Mastership
+     *        Events.
      * @param addedSwitchEvents the collection of added Switch Events.
      * @param removedSwitchEvents the collection of removed Switch Events.
      * @param addedPortEvents the collection of added Port Events.
@@ -90,6 +91,37 @@
     }
 
     /**
+     * Constructor for added events only.
+     *
+     * @param addedMastershipEvents the collection of added Mastership Events.
+     * @param addedSwitchEvents the collection of added Switch Events.
+     * @param addedPortEvents the collection of added Port Events.
+     * @param addedLinkEvents the collection of added Link Events.
+     * @param addedHostEvents the collection of added Host Events.
+     */
+    public TopologyEvents(Collection<MastershipEvent> addedMastershipEvents,
+                          Collection<SwitchEvent> addedSwitchEvents,
+                          Collection<PortEvent> addedPortEvents,
+                          Collection<LinkEvent> addedLinkEvents,
+                          Collection<HostEvent> addedHostEvents) {
+        this.addedMastershipEvents =
+            Collections.unmodifiableCollection(addedMastershipEvents);
+        this.removedMastershipEvents = Collections.emptyList();
+        this.addedSwitchEvents =
+            Collections.unmodifiableCollection(addedSwitchEvents);
+        this.removedSwitchEvents = Collections.emptyList();
+        this.addedPortEvents =
+            Collections.unmodifiableCollection(addedPortEvents);
+        this.removedPortEvents = Collections.emptyList();
+        this.addedLinkEvents =
+            Collections.unmodifiableCollection(addedLinkEvents);
+        this.removedLinkEvents = Collections.emptyList();
+        this.addedHostEvents =
+            Collections.unmodifiableCollection(addedHostEvents);
+        this.removedHostEvents = Collections.emptyList();
+    }
+
+    /**
      * Gets the collection of added Mastership Events.
      *
      * @return the collection of added Mastership Events.
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java b/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
index fcf3caa..a0248b7 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
@@ -4,6 +4,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -364,6 +365,11 @@
     }
 
     @Override
+    public Collection<SwitchEvent> getAllSwitchEvents() {
+        return Collections.unmodifiableCollection(switches.values());
+    }
+
+    @Override
     public PortEvent getPortEvent(final SwitchPort port) {
         ConcurrentMap<PortNumber, PortEvent> portMap = this.ports.get(port.getDpid());
         if (portMap != null) {
@@ -373,6 +379,15 @@
     }
 
     @Override
+    public Collection<PortEvent> getAllPortEvents() {
+        List<PortEvent> events = new LinkedList<>();
+        for (ConcurrentMap<PortNumber, PortEvent> cm : ports.values()) {
+            events.addAll(cm.values());
+        }
+        return Collections.unmodifiableCollection(events);
+    }
+
+    @Override
     public LinkEvent getLinkEvent(final LinkTuple linkId) {
         ConcurrentMap<String, LinkEvent> links = this.outgoingLinks.get(linkId.getSrc());
         if (links == null) {
@@ -411,10 +426,24 @@
     }
 
     @Override
+    public Collection<LinkEvent> getAllLinkEvents() {
+        List<LinkEvent> events = new LinkedList<>();
+        for (ConcurrentMap<String, LinkEvent> cm : outgoingLinks.values()) {
+            events.addAll(cm.values());
+        }
+        return Collections.unmodifiableCollection(events);
+    }
+
+    @Override
     public HostEvent getHostEvent(final MACAddress mac) {
         return this.mac2Host.get(mac);
     }
 
+    @Override
+    public Collection<HostEvent> getAllHostEvents() {
+        return Collections.unmodifiableCollection(mac2Host.values());
+    }
+
     /**
      * Puts a SwitchEvent.
      *
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyInternal.java b/src/main/java/net/onrc/onos/core/topology/TopologyInternal.java
index 10b008e..9656013 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyInternal.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyInternal.java
@@ -11,7 +11,6 @@
  * Interface to reference internal self-contained elements.
  */
 public interface TopologyInternal extends Topology {
-
     /**
      * Gets a SwitchEvent.
      *
@@ -21,6 +20,13 @@
     public SwitchEvent getSwitchEvent(Dpid dpid);
 
     /**
+     * Gets all SwitchEvent entries.
+     *
+     * @return all SwitchEvent entries.
+     */
+    public Collection<SwitchEvent> getAllSwitchEvents();
+
+    /**
      * Gets a PortEvent.
      *
      * @param port Port identifier
@@ -29,6 +35,13 @@
     public PortEvent getPortEvent(SwitchPort port);
 
     /**
+     * Gets all PortEvent entries.
+     *
+     * @return all PortEvent entries.
+     */
+    public Collection<PortEvent> getAllPortEvents();
+
+    /**
      * Gets a LinkEvent.
      *
      * @param linkId Link identifier
@@ -46,14 +59,21 @@
     public LinkEvent getLinkEvent(final LinkTuple linkId, final String type);
 
     /**
-     * Gets a LinkEvent.
+     * Gets a collection of LinkEvent entries.
      *
      * @param linkId Link identifier
-     * @return Collection of LinkEvent
+     * @return Collection of LinkEvent entries.
      */
     public Collection<LinkEvent> getLinkEvents(LinkTuple linkId);
 
     /**
+     * Gets all LinkEvent entries.
+     *
+     * @return all LinkEvent entries.
+     */
+    public Collection<LinkEvent> getAllLinkEvents();
+
+    /**
      * Gets a HostEvent.
      *
      * @param mac MACAddress of the host
@@ -61,4 +81,10 @@
      */
     public HostEvent getHostEvent(MACAddress mac);
 
+    /**
+     * Gets all HostEvent entries.
+     *
+     * @return all HostEvent entries.
+     */
+    public Collection<HostEvent> getAllHostEvents();
 }
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyManager.java b/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
index 1c0f531..57e8b6e 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
@@ -74,6 +74,8 @@
     private TopologyEventPreprocessor eventPreprocessor;
     private CopyOnWriteArrayList<ITopologyListener> topologyListeners =
         new CopyOnWriteArrayList<>();
+    private CopyOnWriteArrayList<ITopologyListener> newTopologyListeners =
+        new CopyOnWriteArrayList<>();
 
     //
     // Metrics
@@ -257,6 +259,11 @@
          */
         private void processEvents(List<EventEntry<TopologyEvent>> events) {
             //
+            // Process pending (new) listeners
+            //
+            processPendingListeners();
+
+            //
             // Pre-process the events
             //
             events = eventPreprocessor.processEvents(events);
@@ -273,6 +280,11 @@
                 // to naturally build and update the topology.
                 //
                 for (EventEntry<TopologyEvent> event : events) {
+                    // Ignore NO-OP events
+                    if (event.isNoop()) {
+                        continue;
+                    }
+
                     TopologyEvent topologyEvent = event.eventData();
 
                     // Get the event itself
@@ -381,6 +393,22 @@
             // NOTE: The ADD and UPDATE events are processed in same way
             entryAdded(value);
         }
+
+        /**
+         * Informs the event handler that a new listener has been added,
+         * and that listener expects the first event to be a snapshot of the
+         * current topology.
+         */
+        void listenerAdded() {
+            //
+            // Generate a NO-OP event so the Event Handler processing can be
+            // triggered to generate in-order a snapshot of the current
+            // topology.
+            // TODO: This is a hack.
+            //
+            EventEntry<TopologyEvent> eventEntry = EventEntry.makeNoop();
+            topologyEvents.add(eventEntry);
+        }
     }
 
     /**
@@ -397,22 +425,89 @@
     }
 
     /**
-     * Registers a listener for topology events.
+     * Adds a listener for topology events.
      *
-     * @param listener the listener to register
+     * @param listener the listener to add.
+     * @param startFromSnapshot if true, and if the topology is not
+     * empty, the first event should be a snapshot of the current topology.
      */
-    void registerTopologyListener(ITopologyListener listener) {
-        topologyListeners.addIfAbsent(listener);
+    void addListener(ITopologyListener listener, boolean startFromSnapshot) {
+        if (startFromSnapshot) {
+            newTopologyListeners.addIfAbsent(listener);
+            eventHandler.listenerAdded();
+        } else {
+            topologyListeners.addIfAbsent(listener);
+        }
     }
 
     /**
-     * Deregisters a listener for topology events. The listener will no longer
+     * Removes a listener for topology events. The listener will no longer
      * receive topology events after this call.
      *
-     * @param listener the listener to deregister
+     * @param listener the listener to remove.
      */
-    void deregisterTopologyListener(ITopologyListener listener) {
+    void removeListener(ITopologyListener listener) {
         topologyListeners.remove(listener);
+        newTopologyListeners.remove(listener);
+    }
+
+    /**
+     * Processes pending (new) listeners.
+     * <p>
+     * During the processing, we dispatch Topology Snapshot Events to new
+     * listeners.
+     */
+    private void processPendingListeners() {
+        if (newTopologyListeners.isEmpty()) {
+            return;
+        }
+
+        //
+        // Create the Topology Snapshot Event
+        //
+        TopologyEvents events = null;
+        List<MastershipEvent> mastershipEvents =
+            new ArrayList<>(lastAddMastershipEvents.values());
+        List<SwitchEvent> switchEvents =
+            new ArrayList<>(topology.getAllSwitchEvents());
+        List<PortEvent> portEvents =
+            new ArrayList<>(topology.getAllPortEvents());
+        List<LinkEvent> linkEvents =
+            new ArrayList<>(topology.getAllLinkEvents());
+        List<HostEvent> hostEvents =
+            new ArrayList<>(topology.getAllHostEvents());
+        if (!(mastershipEvents.isEmpty() &&
+              switchEvents.isEmpty() &&
+              portEvents.isEmpty() &&
+              linkEvents.isEmpty() &&
+              hostEvents.isEmpty())) {
+            events = new TopologyEvents(mastershipEvents,
+                                        switchEvents,
+                                        portEvents,
+                                        linkEvents,
+                                        hostEvents);
+        }
+
+        //
+        // Dispatch Snapshot Event to each new listener, and keep track
+        // of each processed listener.
+        //
+        // NOTE: We deliver the event only if it is not empty.
+        // NOTE: We need to execute the loop so we can properly
+        // move the new listeners together with the older listeners.
+        //
+        List<ITopologyListener> processedListeners = new LinkedList<>();
+        for (ITopologyListener listener : newTopologyListeners) {
+            processedListeners.add(listener);
+            // Move the new listener together with the rest of the listeners
+            topologyListeners.addIfAbsent(listener);
+
+            // Dispatch the event
+            if (events != null) {
+                listener.topologyEvents(events);
+            }
+        }
+        newTopologyListeners.removeAll(processedListeners);
     }
 
     /**
@@ -484,20 +579,29 @@
         this.lastEventTimestampEpochMs = System.currentTimeMillis();
 
         //
+        // Allocate the events to deliver.
+        //
+        // TODO: We could avoid the extra list allocation and copy
+        // by using directly the original list. However, during
+        // the cleanup below, we should create new LinkedList objects
+        // instead of using clear()
+        //
+        TopologyEvents events = new TopologyEvents(
+                new ArrayList<>(apiAddedMastershipEvents),
+                new ArrayList<>(apiRemovedMastershipEvents),
+                new ArrayList<>(apiAddedSwitchEvents),
+                new ArrayList<>(apiRemovedSwitchEvents),
+                new ArrayList<>(apiAddedPortEvents),
+                new ArrayList<>(apiRemovedPortEvents),
+                new ArrayList<>(apiAddedLinkEvents),
+                new ArrayList<>(apiRemovedLinkEvents),
+                new ArrayList<>(apiAddedHostEvents),
+                new ArrayList<>(apiRemovedHostEvents));
+
+        //
         // Deliver the events
         //
         for (ITopologyListener listener : this.topologyListeners) {
-            TopologyEvents events =
-                new TopologyEvents(kryo.copy(apiAddedMastershipEvents),
-                                   kryo.copy(apiRemovedMastershipEvents),
-                                   kryo.copy(apiAddedSwitchEvents),
-                                   kryo.copy(apiRemovedSwitchEvents),
-                                   kryo.copy(apiAddedPortEvents),
-                                   kryo.copy(apiRemovedPortEvents),
-                                   kryo.copy(apiAddedLinkEvents),
-                                   kryo.copy(apiRemovedLinkEvents),
-                                   kryo.copy(apiAddedHostEvents),
-                                   kryo.copy(apiRemovedHostEvents));
             listener.topologyEvents(events);
         }
 
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyModule.java b/src/main/java/net/onrc/onos/core/topology/TopologyModule.java
index d24f4df..fee98b4 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyModule.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyModule.java
@@ -77,12 +77,13 @@
     }
 
     @Override
-    public void registerTopologyListener(ITopologyListener listener) {
-        topologyManager.registerTopologyListener(listener);
+    public void addListener(ITopologyListener listener,
+                            boolean startFromSnapshot) {
+        topologyManager.addListener(listener, startFromSnapshot);
     }
 
     @Override
-    public void deregisterTopologyListener(ITopologyListener listener) {
-        topologyManager.deregisterTopologyListener(listener);
+    public void removeListener(ITopologyListener listener) {
+        topologyManager.removeListener(listener);
     }
 }
diff --git a/src/main/java/net/onrc/onos/core/util/EventEntry.java b/src/main/java/net/onrc/onos/core/util/EventEntry.java
index e1abcaf..d19a1ac 100644
--- a/src/main/java/net/onrc/onos/core/util/EventEntry.java
+++ b/src/main/java/net/onrc/onos/core/util/EventEntry.java
@@ -8,12 +8,13 @@
      * The event types.
      */
     public enum Type {
-        ENTRY_ADD,            // Add or update an entry
-        ENTRY_REMOVE            // Remove an entry
+        ENTRY_ADD,              // Add or update an entry
+        ENTRY_REMOVE,           // Remove an entry
+        ENTRY_NOOP              // NO-OP event (No Operation)
     }
 
-    private Type eventType;    // The event type
-    private T eventData;    // The relevant event data entry
+    private Type eventType;     // The event type
+    private T eventData;        // The relevant event data entry
 
     /**
      * Constructor for a given event type and event-related data entry.
@@ -27,7 +28,22 @@
     }
 
     /**
-     * Test whether the event type is ENTRY_ADD.
+     * Creates a NO-OP event.
+     * <p/>
+     * This is a factory method that can be called without an object:
+     * <p/>
+     * <code>
+     *   EventEntry<TopologyEvent> eventEntry = EventEntry.makeNoop();
+     * </code>
+     *
+     * @return a NO-OP event.
+     */
+    public static <T> EventEntry<T> makeNoop() {
+        return new EventEntry<T>(Type.ENTRY_NOOP, null);
+    }
+
+    /**
+     * Tests whether the event type is ENTRY_ADD.
      *
      * @return true if the event type is ENTRY_ADD, otherwise false.
      */
@@ -36,7 +52,7 @@
     }
 
     /**
-     * Test whether the event type is ENTRY_REMOVE.
+     * Tests whether the event type is ENTRY_REMOVE.
      *
      * @return true if the event type is ENTRY_REMOVE, otherwise false.
      */
@@ -45,7 +61,16 @@
     }
 
     /**
-     * Get the event type.
+     * Tests whether the event type is ENTRY_NOOP.
+     *
+     * @return true if the event type is ENTRY_NOOP, otherwise false.
+     */
+    public boolean isNoop() {
+        return (this.eventType == Type.ENTRY_NOOP);
+    }
+
+    /**
+     * Gets the event type.
      *
      * @return the event type.
      */
@@ -54,7 +79,7 @@
     }
 
     /**
-     * Get the event-related data entry.
+     * Gets the event-related data entry.
      *
      * @return the event-related data entry.
      */
diff --git a/src/test/java/net/onrc/onos/core/hostmanager/HostManagerTest.java b/src/test/java/net/onrc/onos/core/hostmanager/HostManagerTest.java
index 7e552ed..3c4bfee 100644
--- a/src/test/java/net/onrc/onos/core/hostmanager/HostManagerTest.java
+++ b/src/test/java/net/onrc/onos/core/hostmanager/HostManagerTest.java
@@ -86,7 +86,7 @@
         controllerRegistryService = createMock(IControllerRegistryService.class);
         eventChannel = createMock(IEventChannel.class);
         expect(networkGraphService.getTopology()).andReturn(topology).anyTimes();
-        networkGraphService.registerTopologyListener(anyObject(ITopologyListener.class));
+        networkGraphService.addListener(anyObject(ITopologyListener.class), eq(false));
         expectLastCall();
 
         expect(datagridService.createChannel("onos.host", Long.class, Host.class))
diff --git a/src/test/java/net/onrc/onos/core/intent/runtime/IntentTestMocks.java b/src/test/java/net/onrc/onos/core/intent/runtime/IntentTestMocks.java
index a6dde8b..486604c 100644
--- a/src/test/java/net/onrc/onos/core/intent/runtime/IntentTestMocks.java
+++ b/src/test/java/net/onrc/onos/core/intent/runtime/IntentTestMocks.java
@@ -88,8 +88,8 @@
 
             expect(topologyService.getTopology()).andReturn(topology)
                     .anyTimes();
-            topologyService.registerTopologyListener(
-                    anyObject(ITopologyListener.class));
+            topologyService.addListener(
+                    anyObject(ITopologyListener.class), eq(false));
             expectLastCall();
 
             expect(datagridService.createChannel("onos.pathintent",
diff --git a/src/test/java/net/onrc/onos/core/intent/runtime/UseCaseTest.java b/src/test/java/net/onrc/onos/core/intent/runtime/UseCaseTest.java
index 1d85984..69f8e01 100644
--- a/src/test/java/net/onrc/onos/core/intent/runtime/UseCaseTest.java
+++ b/src/test/java/net/onrc/onos/core/intent/runtime/UseCaseTest.java
@@ -106,7 +106,8 @@
                 .andReturn(restApi).once();
 
         expect(topologyService.getTopology()).andReturn(mockTopology).anyTimes();
-        topologyService.registerTopologyListener(anyObject(ITopologyListener.class));
+        topologyService.addListener(anyObject(ITopologyListener.class),
+                                    eq(false));
         expectLastCall();
 
         expect(datagridService.createChannel("onos.pathintent", Long.class, IntentOperationList.class))
diff --git a/src/test/java/net/onrc/onos/core/topology/TopologyManagerTest.java b/src/test/java/net/onrc/onos/core/topology/TopologyManagerTest.java
index 231ba6f..a84b00f 100644
--- a/src/test/java/net/onrc/onos/core/topology/TopologyManagerTest.java
+++ b/src/test/java/net/onrc/onos/core/topology/TopologyManagerTest.java
@@ -145,14 +145,10 @@
 
         // Setup the Registry Service
         expect(registryService.getOnosInstanceId()).andReturn(ONOS_INSTANCE_ID_1).anyTimes();
-        try {
-            expect(registryService.getControllerForSwitch(DPID_1.value())).
-                andReturn(ONOS_INSTANCE_ID_1.toString()).anyTimes();
-            expect(registryService.getControllerForSwitch(DPID_2.value())).
-                andReturn(ONOS_INSTANCE_ID_2.toString()).anyTimes();
-        } catch (RegistryException ex) {
-            throw new IllegalStateException(ex);
-        }
+        expect(registryService.getControllerForSwitch(DPID_1.value()))
+            .andReturn(ONOS_INSTANCE_ID_1.toString()).anyTimes();
+        expect(registryService.getControllerForSwitch(DPID_2.value()))
+            .andReturn(ONOS_INSTANCE_ID_2.toString()).anyTimes();
 
         allTopologyEvents = new CopyOnWriteArrayList<>();
         expect(eventChannel.getAllEntries())
@@ -186,7 +182,7 @@
     private void setupTopologyManagerWithEventHandler() {
         // Create a TopologyManager object for testing
         theTopologyManager = new TopologyManager(registryService);
-        theTopologyManager.registerTopologyListener(theTopologyListener);
+        theTopologyManager.addListener(theTopologyListener, true);
 
         // Allocate the Event Handler, so we can have direct access to it
         theEventHandler = theTopologyManager.new EventHandler();
@@ -1432,9 +1428,12 @@
      *   instances are processed - both events should be delivered.
      * - Finally, a REMOVE Switch Event is received from the first ONOS
      *   instance - no event should be delivered.
+     *
+     * @throws RegistryException
      */
     @Test
-    public void testProcessSwitchMastershipSwitchover() {
+    public void testProcessSwitchMastershipSwitchover()
+                        throws RegistryException {
         TopologyEvents topologyEvents;
         List<EventEntry<TopologyEvent>> events = new LinkedList<>();
         EventEntry<TopologyEvent> eventEntry;
@@ -1484,12 +1483,8 @@
         // Master.
         //
         reset(registryService);
-        try {
-            expect(registryService.getControllerForSwitch(DPID_1.value())).
-                andReturn(ONOS_INSTANCE_ID_2.toString()).anyTimes();
-        } catch (RegistryException ex) {
-            throw new IllegalStateException(ex);
-        }
+        expect(registryService.getControllerForSwitch(DPID_1.value()))
+            .andReturn(ONOS_INSTANCE_ID_2.toString()).anyTimes();
         replay(registryService);
 
         // Prepare the Mastership Event from the second ONOS instance