Network Graph Refactoring: WIP: Deliver notifications to the Network Graph Listeners
 - Deliver notifications to the Network Graph Listeners
 - Refactor the Network Graph Listener API:
   If there is any topology change, the listener will get a single API call:
   networkGraphEvents() which contains all added/removed
   switch/port/link/events
 - Misc. refactoring inside the TopologyManager

Change-Id: I7da6c4775347aa2fd480365784215bb6bd34e398
diff --git a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
index df927f6..2c269fd 100755
--- a/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
+++ b/src/main/java/net/onrc/onos/intent/runtime/PathCalcRuntimeModule.java
@@ -152,42 +152,18 @@
 	}
 
 	@Override
-	public void putSwitchEvent(SwitchEvent switchEvent) {
-		// do nothing
-	}
-
-	@Override
-	public void removeSwitchEvent(SwitchEvent switchEvent) {
-		// do nothing
-	}
-
-	@Override
-	public void putPortEvent(PortEvent portEvent) {
-		// do nothing
-	}
-
-	@Override
-	public void removePortEvent(PortEvent portEvent) {
-		// do nothing
-	}
-
-	@Override
-	public void putLinkEvent(LinkEvent linkEvent) {
-		// do nothing
-	}
-
-	@Override
-	public void removeLinkEvent(LinkEvent linkEvent) {
+	public void networkGraphEvents(
+				Collection<SwitchEvent> addedSwitchEvents,
+				Collection<SwitchEvent> removedSwitchEvents,
+				Collection<PortEvent> addedPortEvents,
+				Collection<PortEvent> removedPortEvents,
+				Collection<LinkEvent> addedLinkEvents,
+				Collection<LinkEvent> removedLinkEvents,
+				Collection<DeviceEvent> addedDeviceEvents,
+				Collection<DeviceEvent> removedDeviceEvents) {
+	    // TODO: The implementation below is incomplete
+	    for (LinkEvent linkEvent : removedLinkEvents) {
 		reroutePaths(linkEvent);
-	}
-
-	@Override
-	public void putDeviceEvent(DeviceEvent deviceEvent) {
-		// do nothing
-	}
-
-	@Override
-	public void removeDeviceEvent(DeviceEvent deviceEvent) {
-		// do nothing
+	    }
 	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/INetworkGraphListener.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/INetworkGraphListener.java
index 88806ef..dfdba42 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/INetworkGraphListener.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/INetworkGraphListener.java
@@ -1,22 +1,30 @@
 package net.onrc.onos.ofcontroller.networkgraph;
 
+import java.util.Collection;
+
 /**
  * Interface which needs to be implemented to receive Topology events from
- * NetworkGraph
- *
- * TODO Should these interface hand over Event object or Object in NetworkGraph.
+ * the NetworkGraph.
  */
 public interface INetworkGraphListener {
-    public void putSwitchEvent(SwitchEvent switchEvent);
-    public void removeSwitchEvent(SwitchEvent switchEvent);
-
-    public void putPortEvent(PortEvent portEvent);
-    public void removePortEvent(PortEvent portEvent);
-
-    public void putLinkEvent(LinkEvent linkEvent);
-    public void removeLinkEvent(LinkEvent linkEvent);
-
-    public void putDeviceEvent(DeviceEvent deviceEvent);
-    public void removeDeviceEvent(DeviceEvent deviceEvent);
-
+    /**
+     * Network Graph events.
+     *
+     * @param addedSwitchEvents the Added Switch Events.
+     * @param removedSwitchEvents the Removed Switch Events.
+     * @param addedPortEvents the Added Port Events.
+     * @param removedPortEvents the Removed Port Events.
+     * @param addedLinkEvents the Added Link Events.
+     * @param removedLinkEvents the Removed Link Events.
+     * @param addedDeviceEvents the Added Device Events.
+     * @param removedDeviceEvents the Removed Device Events.
+     */
+    public void networkGraphEvents(Collection<SwitchEvent> addedSwitchEvents,
+				   Collection<SwitchEvent> removedSwitchEvents,
+				   Collection<PortEvent> addedPortEvents,
+				   Collection<PortEvent> removedPortEvents,
+				   Collection<LinkEvent> addedLinkEvents,
+				   Collection<LinkEvent> removedLinkEvents,
+				   Collection<DeviceEvent> addedDeviceEvents,
+				   Collection<DeviceEvent> removedDeviceEvents);
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/TopologyManager.java b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/TopologyManager.java
index 7377ce6..47022f1 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/networkgraph/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/networkgraph/TopologyManager.java
@@ -67,6 +67,18 @@
     private Map<ByteBuffer, DeviceEvent> reorderedAddedDeviceEvents =
 	new HashMap<ByteBuffer, DeviceEvent>();
 
+    //
+    // Local state for keeping track of the application event notifications
+    //
+    List<SwitchEvent> apiAddedSwitchEvents = new LinkedList<SwitchEvent>();
+    List<SwitchEvent> apiRemovedSwitchEvents = new LinkedList<SwitchEvent>();
+    List<PortEvent> apiAddedPortEvents = new LinkedList<PortEvent>();
+    List<PortEvent> apiRemovedPortEvents = new LinkedList<PortEvent>();
+    List<LinkEvent> apiAddedLinkEvents = new LinkedList<LinkEvent>();
+    List<LinkEvent> apiRemovedLinkEvents = new LinkedList<LinkEvent>();
+    List<DeviceEvent> apiAddedDeviceEvents = new LinkedList<DeviceEvent>();
+    List<DeviceEvent> apiRemovedDeviceEvents = new LinkedList<DeviceEvent>();
+
     /**
      * Constructor.
      *
@@ -275,37 +287,15 @@
 		removeSwitch(switchEvent);
 
 	    //
-	    // Try to apply the reordered events.
+	    // Apply reordered events
 	    //
-	    // NOTE: For simplicity we try to apply all events of a particular
-	    // type if any "parent" type event was processed:
-	    //  - Apply reordered Port Events if Switches were added
-	    //  - Apply reordered Link and Device Events if Switches or Ports
-	    //    were added
+	    applyReorderedEvents(! addedSwitchEvents.isEmpty(),
+				 ! addedPortEvents.isEmpty());
+
 	    //
-	    if (! (addedSwitchEvents.isEmpty() && addedPortEvents.isEmpty())) {
-		Map<ByteBuffer, PortEvent> portEvents = reorderedAddedPortEvents;
-		Map<ByteBuffer, LinkEvent> linkEvents = reorderedAddedLinkEvents;
-		Map<ByteBuffer, DeviceEvent> deviceEvents = reorderedAddedDeviceEvents;
-		reorderedAddedPortEvents = new HashMap<>();
-		reorderedAddedLinkEvents = new HashMap<>();
-		reorderedAddedDeviceEvents = new HashMap<>();
-		//
-		// Apply reordered Port Events if Switches were added
-		//
-		if (! addedSwitchEvents.isEmpty()) {
-		    for (PortEvent portEvent : portEvents.values())
-			addPort(portEvent);
-		}
-		//
-		// Apply reordered Link and Device Events if Switches or Ports
-		// were added.
-		//
-		for (LinkEvent linkEvent : linkEvents.values())
-		    addLink(linkEvent);
-		for (DeviceEvent deviceEvent : deviceEvents.values())
-		    addDevice(deviceEvent);
-	    }
+	    // Dispatch the Topology Notification Events to the applications
+	    //
+	    dispatchNetworkGraphEvents();
 	}
 
 	/**
@@ -359,6 +349,90 @@
 	eventHandler.start();
     }
 
+    /**
+     * Dispatch Network Graph Events to the listeners.
+     */
+    private void dispatchNetworkGraphEvents() {
+	if (apiAddedSwitchEvents.isEmpty() &&
+	    apiRemovedSwitchEvents.isEmpty() &&
+	    apiAddedPortEvents.isEmpty() &&
+	    apiRemovedPortEvents.isEmpty() &&
+	    apiAddedLinkEvents.isEmpty() &&
+	    apiRemovedLinkEvents.isEmpty() &&
+	    apiAddedDeviceEvents.isEmpty() &&
+	    apiRemovedDeviceEvents.isEmpty()) {
+	    return;		// No events to dispatch
+	}
+
+	// Deliver the events
+	for (INetworkGraphListener listener : this.networkGraphListeners) {
+	    // TODO: Should copy before handing them over to listener?
+	    listener.networkGraphEvents(apiAddedSwitchEvents,
+					apiRemovedSwitchEvents,
+					apiAddedPortEvents,
+					apiRemovedPortEvents,
+					apiAddedLinkEvents,
+					apiRemovedLinkEvents,
+					apiAddedDeviceEvents,
+					apiRemovedDeviceEvents);
+	}
+
+	//
+	// Cleanup
+	//
+	apiAddedSwitchEvents.clear();
+	apiRemovedSwitchEvents.clear();
+	apiAddedPortEvents.clear();
+	apiRemovedPortEvents.clear();
+	apiAddedLinkEvents.clear();
+	apiRemovedLinkEvents.clear();
+	apiAddedDeviceEvents.clear();
+	apiRemovedDeviceEvents.clear();
+    }
+
+    /**
+     * Apply reordered events.
+     *
+     * @param hasAddedSwitchEvents true if there were Added Switch Events.
+     * @param hasAddedPortEvents true if there were Added Port Events.
+     */
+    private void applyReorderedEvents(boolean hasAddedSwitchEvents,
+				      boolean hasAddedPortEvents) {
+	if (! (hasAddedSwitchEvents || hasAddedPortEvents))
+	    return;		// Nothing to do
+
+	//
+	// Try to apply the reordered events.
+	//
+	// NOTE: For simplicity we try to apply all events of a particular
+	// type if any "parent" type event was processed:
+	//  - Apply reordered Port Events if Switches were added
+	//  - Apply reordered Link and Device Events if Switches or Ports
+	//    were added
+	//
+	Map<ByteBuffer, PortEvent> portEvents = reorderedAddedPortEvents;
+	Map<ByteBuffer, LinkEvent> linkEvents = reorderedAddedLinkEvents;
+	Map<ByteBuffer, DeviceEvent> deviceEvents = reorderedAddedDeviceEvents;
+	reorderedAddedPortEvents = new HashMap<>();
+	reorderedAddedLinkEvents = new HashMap<>();
+	reorderedAddedDeviceEvents = new HashMap<>();
+	//
+	// Apply reordered Port Events if Switches were added
+	//
+	if (hasAddedSwitchEvents) {
+	    for (PortEvent portEvent : portEvents.values())
+		addPort(portEvent);
+	}
+	//
+	// Apply reordered Link and Device Events if Switches or Ports
+	// were added.
+	//
+	for (LinkEvent linkEvent : linkEvents.values())
+	    addLink(linkEvent);
+	for (DeviceEvent deviceEvent : deviceEvents.values())
+	    addDevice(deviceEvent);
+    }
+
     /* ******************************
      * NetworkGraphDiscoveryInterface methods
      * ******************************/
@@ -447,21 +521,22 @@
     /* ************************************************
      * Internal methods to maintain the network graph
      * ************************************************/
-    private void addSwitch(SwitchEvent swEvent) {
-	Switch sw = networkGraph.getSwitch(swEvent.getDpid());
+    private void addSwitch(SwitchEvent switchEvent) {
+	Switch sw = networkGraph.getSwitch(switchEvent.getDpid());
 	if (sw == null) {
-	    sw = new SwitchImpl(networkGraph, swEvent.getDpid());
+	    sw = new SwitchImpl(networkGraph, switchEvent.getDpid());
 	    networkGraph.putSwitch(sw);
 	} else {
 	    // TODO: Update the switch attributes
 	    // TODO: Nothing to do for now
 	}
+	apiAddedSwitchEvents.add(switchEvent);
     }
 
-    private void removeSwitch(SwitchEvent swEvent) {
-	Switch sw = networkGraph.getSwitch(swEvent.getDpid());
+    private void removeSwitch(SwitchEvent switchEvent) {
+	Switch sw = networkGraph.getSwitch(switchEvent.getDpid());
 	if (sw == null) {
-	    log.warn("Switch {} already removed, ignoring", swEvent);
+	    log.warn("Switch {} already removed, ignoring", switchEvent);
 	    return;
 	}
 
@@ -471,7 +546,7 @@
 	ArrayList<PortEvent> portsToRemove = new ArrayList<>();
 	for (Port port : sw.getPorts()) {
 	    log.warn("Port {} on Switch {} should be removed prior to removing Switch. Removing Port now.",
-		     port, swEvent);
+		     port, switchEvent);
 	    PortEvent portEvent = new PortEvent(port.getDpid(),
 						port.getNumber());
 	    portsToRemove.add(portEvent);
@@ -479,7 +554,8 @@
 	for (PortEvent portEvent : portsToRemove)
 	    removePort(portEvent);
 
-	networkGraph.removeSwitch(swEvent.getDpid());
+	networkGraph.removeSwitch(switchEvent.getDpid());
+	apiRemovedSwitchEvents.add(switchEvent);
     }
 
     private void addPort(PortEvent portEvent) {
@@ -499,6 +575,7 @@
 	} else {
 	    // TODO: Update the port attributes
 	}
+	apiAddedPortEvents.add(portEvent);
     }
 
     private void removePort(PortEvent portEvent) {
@@ -553,6 +630,8 @@
 	// Remove the Port from the Switch
 	SwitchImpl switchImpl = getSwitchImpl(sw);
 	switchImpl.removePort(port);
+
+	apiRemovedPortEvents.add(portEvent);
     }
 
     private void addLink(LinkEvent linkEvent) {
@@ -600,6 +679,8 @@
 	} else {
 	    // TODO: Update the link attributes
 	}
+
+	apiAddedLinkEvents.add(linkEvent);
     }
 
     private void removeLink(LinkEvent linkEvent) {
@@ -633,6 +714,8 @@
 	}
 	getPortImpl(dstPort).setIncomingLink(null);
 	getPortImpl(srcPort).setOutgoingLink(null);
+
+	apiRemovedLinkEvents.add(linkEvent);
     }
 
     // TODO: Device-related work is incomplete
@@ -675,8 +758,10 @@
 	}
 
 	// Update the device in the Network Graph
-	if (attachmentFound)
+	if (attachmentFound) {
 	    networkGraph.putDevice(device);
+	    apiAddedDeviceEvents.add(deviceEvent);
+	}
     }
 
     private void removeDevice(DeviceEvent deviceEvent) {
@@ -701,20 +786,22 @@
 	    portImpl.removeDevice(device);
 	    deviceImpl.removeAttachmentPoint(port);
 	}
+
 	networkGraph.removeDevice(device);
+	apiRemovedDeviceEvents.add(deviceEvent);
     }
 
     /**
      *
-     * @param swEvent
+     * @param switchEvent
      * @return true if ready to accept event.
      */
-    private boolean prepareForAddSwitchEvent(SwitchEvent swEvent) {
+    private boolean prepareForAddSwitchEvent(SwitchEvent switchEvent) {
 	// No show stopping precondition
 	return true;
     }
 
-    private boolean prepareForRemoveSwitchEvent(SwitchEvent swEvent) {
+    private boolean prepareForRemoveSwitchEvent(SwitchEvent switchEvent) {
 	// No show stopping precondition
 	return true;
     }
@@ -888,62 +975,6 @@
 	return true;
     }
 
-    private void dispatchPutSwitchEvent(SwitchEvent switchEvent) {
-	for (INetworkGraphListener listener : this.networkGraphListeners) {
-	    // TODO Should copy before handing them over to listener
-	    listener.putSwitchEvent(switchEvent);
-	}
-    }
-
-    private void dispatchRemoveSwitchEvent(SwitchEvent switchEvent) {
-	for (INetworkGraphListener listener : this.networkGraphListeners) {
-	    // TODO Should copy before handing them over to listener
-	    listener.removeSwitchEvent(switchEvent);
-	}
-    }
-
-    private void dispatchPutPortEvent(PortEvent portEvent) {
-	for (INetworkGraphListener listener : this.networkGraphListeners) {
-	    // TODO Should copy before handing them over to listener
-	    listener.putPortEvent(portEvent);
-	}
-    }
-
-    private void dispatchRemovePortEvent(PortEvent portEvent) {
-	for (INetworkGraphListener listener : this.networkGraphListeners) {
-	    // TODO Should copy before handing them over to listener
-	    listener.removePortEvent(portEvent);
-	}
-    }
-
-    private void dispatchPutLinkEvent(LinkEvent linkEvent) {
-	for (INetworkGraphListener listener : this.networkGraphListeners) {
-	    // TODO Should copy before handing them over to listener
-	    listener.putLinkEvent(linkEvent);
-	}
-    }
-
-    private void dispatchRemoveLinkEvent(LinkEvent linkEvent) {
-	for (INetworkGraphListener listener : this.networkGraphListeners) {
-	    // TODO Should copy before handing them over to listener
-	    listener.removeLinkEvent(linkEvent);
-	}
-    }
-
-    private void dispatchPutDeviceEvent(DeviceEvent deviceEvent) {
-	for (INetworkGraphListener listener : this.networkGraphListeners) {
-	    // TODO Should copy before handing them over to listener
-	    listener.putDeviceEvent(deviceEvent);;
-	}
-    }
-
-    private void dispatchRemoveDeviceEvent(DeviceEvent deviceEvent) {
-	for (INetworkGraphListener listener : this.networkGraphListeners) {
-	    // TODO Should copy before handing them over to listener
-	    listener.removeDeviceEvent(deviceEvent);
-	}
-    }
-
     private SwitchImpl getSwitchImpl(Switch sw) {
 	if (sw instanceof SwitchImpl) {
 	    return (SwitchImpl) sw;
diff --git a/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java b/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java
index 36162da..d060cf1 100755
--- a/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java
+++ b/src/test/java/net/onrc/onos/intent/runtime/UseCaseTest.java
@@ -1,5 +1,6 @@
 package net.onrc.onos.intent.runtime;
 
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
@@ -19,7 +20,10 @@
 import net.onrc.onos.intent.persist.PersistIntent;
 import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphListener;
 import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphService;
+import net.onrc.onos.ofcontroller.networkgraph.DeviceEvent;
 import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
+import net.onrc.onos.ofcontroller.networkgraph.PortEvent;
+import net.onrc.onos.ofcontroller.networkgraph.SwitchEvent;
 import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
 import net.onrc.onos.registry.controller.IControllerRegistryService;
 
@@ -184,6 +188,15 @@
 
 	@Test
 	public void rerouteShortestPaths() throws FloodlightModuleException {
+		List<SwitchEvent> addedSwitchEvents = new LinkedList<>();
+		List<SwitchEvent> removedSwitchEvents = new LinkedList<>();
+		List<PortEvent> addedPortEvents = new LinkedList<>();
+		List<PortEvent> removedPortEvents = new LinkedList<>();
+		List<LinkEvent> addedLinkEvents = new LinkedList<>();
+		List<LinkEvent> removedLinkEvents = new LinkedList<>();
+		List<DeviceEvent> addedDeviceEvents = new LinkedList<>();
+		List<DeviceEvent> removedDeviceEvents = new LinkedList<>();
+
 		// create shortest path intents
 		IntentOperationList opList = new IntentOperationList();
 		opList.add(Operator.ADD, new ShortestPathIntent("1", 1L, 20L, 1L, 4L, 20L, 4L));
@@ -207,11 +220,29 @@
 		// link down
 		((MockNetworkGraph)g).removeLink(1L, 2L, 9L, 1L); // This link is used by the intent "1"
 		LinkEvent linkEvent = new LinkEvent(1L, 2L, 9L, 1L);
-		runtime1.removeLinkEvent(linkEvent);
+		removedLinkEvents.clear();
+		removedLinkEvents.add(linkEvent);
+		runtime1.networkGraphEvents(addedSwitchEvents,
+					    removedSwitchEvents,
+					    addedPortEvents,
+					    removedPortEvents,
+					    addedLinkEvents,
+					    removedLinkEvents,
+					    addedDeviceEvents,
+					    removedDeviceEvents);
+
 		((MockNetworkGraph)g).removeLink(9L, 1L, 1L, 2L);
 		linkEvent = new LinkEvent(9L, 1L, 1L, 2L);
-		runtime1.removeLinkEvent(linkEvent);
-
+		removedLinkEvents.clear();
+		removedLinkEvents.add(linkEvent);
+		runtime1.networkGraphEvents(addedSwitchEvents,
+					    removedSwitchEvents,
+					    addedPortEvents,
+					    removedPortEvents,
+					    addedLinkEvents,
+					    removedLinkEvents,
+					    addedDeviceEvents,
+					    removedDeviceEvents);
 		System.out.println("Link goes down.");
 
 		// show results step2