Merge branch 'master' of https://github.com/OPENNETWORKINGLAB/ONOS
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
index ecf217e..f217c25 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyService.java
@@ -1,6 +1,7 @@
 package net.floodlightcontroller.core;
 
 import java.util.List;
+import java.util.Map;
 
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.core.INetMapTopologyObjects.IDeviceObject;
@@ -47,7 +48,7 @@
 
 	    /**
 	     * Fetch the Switch and Ports info from the Titan Graph
-	     * and store it locally for fast access during the shortest path
+	     * and return it for fast access during the shortest path
 	     * computation.
 	     *
 	     * After fetching the state, method @ref getTopoShortestPath()
@@ -63,14 +64,17 @@
 	     * method @ref dropShortestPathTopo() should be used to release
 	     * the internal state that is not needed anymore:
 	     *
-	     *       prepareShortestPathTopo();
+	     *       Map<Long, ?> shortestPathTopo;
+	     *       shortestPathTopo = prepareShortestPathTopo();
 	     *       for (int i = 0; i < 10000; i++) {
-	     *           dataPath = getTopoShortestPath(...);
+	     *           dataPath = getTopoShortestPath(shortestPathTopo, ...);
 	     *           ...
 	     *        }
-	     *        dropShortestPathTopo();
+	     *        dropShortestPathTopo(shortestPathTopo);
+	     *
+	     * @return the Shortest Path info handler stored in a map.
 	     */
-	    void prepareShortestPathTopo();
+	    Map<Long, ?> prepareShortestPathTopo();
 
 	    /**
 	     * Release the state that was populated by
@@ -78,8 +82,10 @@
 	     *
 	     * See the documentation for method @ref prepareShortestPathTopo()
 	     * for additional information and usage.
+	     *
+	     * @shortestPathTopo the Shortest Path info handler to release.
 	     */
-	    void dropShortestPathTopo();
+	    void dropShortestPathTopo(Map<Long, ?> shortestPathTopo);
 
 	    /**
 	     * Get the shortest path from a source to a destination by
@@ -89,12 +95,15 @@
 	     * See the documentation for method @ref prepareShortestPathTopo()
 	     * for additional information and usage.
 	     *
+	     * @paran shortestPathTopoHandler the Shortest Path info handler
+	     * to use.
 	     * @param src the source in the shortest path computation.
 	     * @param dest the destination in the shortest path computation.
 	     * @return the data path with the computed shortest path if
 	     * found, otherwise null.
 	     */
-	    DataPath getTopoShortestPath(SwitchPort src, SwitchPort dest);
+	    DataPath getTopoShortestPath(Map<Long, ?> shortestPathTopo,
+					 SwitchPort src, SwitchPort dest);
 
 	    /**
 	     * Test whether a route exists from a source to a destination.
diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java b/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java
index 1bc258b..f8dd52c 100644
--- a/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java
+++ b/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java
@@ -17,6 +17,8 @@
 
 package net.floodlightcontroller.core;
 
+import org.openflow.protocol.OFPhysicalPort;
+
 /**
  *
  *
@@ -44,6 +46,16 @@
     public void switchPortChanged(Long switchId);
     
     /**
+     * Fired when ports on a switch area added
+     */
+    public void switchPortAdded(Long switchId, OFPhysicalPort port);
+    
+    /**
+     * Fired when ports on a switch area removed
+     */
+    public void switchPortRemoved(Long switchId, OFPhysicalPort port);
+    
+    /**
      * The name assigned to this listener
      * @return
      */
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index 0d49c03..842ef35 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -147,7 +147,6 @@
 public class Controller implements IFloodlightProviderService, 
             IStorageSourceListener {
    	
-	protected SwitchStorageImpl swStore;;
 	
     protected static Logger log = LoggerFactory.getLogger(Controller.class);
 
@@ -265,18 +264,26 @@
     public enum SwitchUpdateType {
         ADDED,
         REMOVED,
-        PORTCHANGED
+        PORTCHANGED,
+        PORTADDED,
+        PORTREMOVED
     }
     /**
      * Update message indicating a switch was added or removed 
      */
     protected class SwitchUpdate implements IUpdate {
         public IOFSwitch sw;
+        public OFPhysicalPort port;
         public SwitchUpdateType switchUpdateType;
         public SwitchUpdate(IOFSwitch sw, SwitchUpdateType switchUpdateType) {
             this.sw = sw;
             this.switchUpdateType = switchUpdateType;
         }
+        public SwitchUpdate(IOFSwitch sw, OFPhysicalPort port, SwitchUpdateType switchUpdateType) {
+            this.sw = sw;
+            this.port = port;
+            this.switchUpdateType = switchUpdateType;
+        }
         public void dispatch() {
             if (log.isTraceEnabled()) {
                 log.trace("Dispatching switch update {} {}",
@@ -294,6 +301,14 @@
                         case PORTCHANGED:
                             listener.switchPortChanged(sw.getId());
                             break;
+                        case PORTADDED:
+                        	listener.switchPortAdded(sw.getId(), port);
+                        	break;
+                        case PORTREMOVED:
+                        	listener.switchPortRemoved(sw.getId(), port);
+                        	break;
+                        default:
+                        	break;
                     }
                 }
             }
@@ -1269,23 +1284,43 @@
             		((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0);
             sw.setPort(port);
            if (!portDown) {
-               swStore.addPort(sw.getStringId(), port);
+               SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
+               try {
+                   this.updates.put(update);
+               } catch (InterruptedException e) {
+                   log.error("Failure adding update to queue", e);
+               }
            } else {
-        	   swStore.deletePort(sw.getStringId(), port.getPortNumber());
+               SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
+               try {
+                   this.updates.put(update);
+               } catch (InterruptedException e) {
+                   log.error("Failure adding update to queue", e);
+               }
            }
             if (updateStorage)
                 updatePortInfo(sw, port);
             log.debug("Port #{} modified for {}", portNumber, sw);
         } else if (m.getReason() == (byte)OFPortReason.OFPPR_ADD.ordinal()) {
             sw.setPort(port);
-            swStore.addPort(sw.getStringId(), port);
+            SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
+            try {
+                this.updates.put(update);
+            } catch (InterruptedException e) {
+                log.error("Failure adding update to queue", e);
+            }
             if (updateStorage)
                 updatePortInfo(sw, port);
             log.debug("Port #{} added for {}", portNumber, sw);
         } else if (m.getReason() == 
                    (byte)OFPortReason.OFPPR_DELETE.ordinal()) {
             sw.deletePort(portNumber);
-            swStore.deletePort(sw.getStringId(), portNumber);
+            SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
+            try {
+                this.updates.put(update);
+            } catch (InterruptedException e) {
+                log.error("Failure adding update to queue", e);
+            }
             if (updateStorage)
                 removePortInfo(sw, portNumber);
             log.debug("Port #{} deleted for {}", portNumber, sw);
@@ -1559,12 +1594,6 @@
         }
         
         updateActiveSwitchInfo(sw);
-        if (registryService.hasControl(sw.getId())) {
-        	swStore.update(sw.getStringId(), SwitchState.ACTIVE, DM_OPERATION.UPDATE);
-        	for (OFPhysicalPort port: sw.getPorts()) {
-        		swStore.addPort(sw.getStringId(), port);
-        	}
-        }
         SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.ADDED);
         try {
             this.updates.put(update);
@@ -1584,14 +1613,6 @@
         // this method is only called after netty has processed all
         // pending messages
         log.debug("removeSwitch: {}", sw);
- //
- //     Cannot set sw to inactive in network map due to race condition
- //     Need a cleanup thread to periodically check switches not active in registry
- //     and acquire control to set to inactive state in network map and release it       
- //
- //       if (registryService.hasControl(sw.getId())) {
- //      	swStore.update(sw.getStringId(), SwitchState.INACTIVE, DM_OPERATION.UPDATE);
- //       }
         if (!this.activeSwitches.remove(sw.getId(), sw) || !sw.isConnected()) {
             log.debug("Not removing switch {}; already removed", sw);
             return;
@@ -2220,8 +2241,6 @@
 			log.debug("did not get DB config setting using default {}", conf);
 		}
 		log.debug("setting DB config {}", conf);
-		this.swStore = new SwitchStorageImpl();
-		this.swStore.init(conf);
 		
         initVendorMessages();
         this.systemStartTime = System.currentTimeMillis();
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 57c136c..8aec20f 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -98,6 +98,8 @@
     private LinkedList<FlowPath> measurementStoredPaths = new LinkedList<FlowPath>();
     private long measurementStartTimeProcessingPaths = 0;
     private long measurementEndTimeProcessingPaths = 0;
+    Map<Long, ?> measurementShortestPathTopo = null;
+    private String measurementPerFlowStr = new String();
 
     /** The logger. */
     private static Logger log = LoggerFactory.getLogger(FlowManager.class);
@@ -277,7 +279,8 @@
 		// Fetch and recompute the Shortest Path for those
 		// Flow Paths this controller is responsible for.
 		//
-		topoRouteService.prepareShortestPathTopo();
+		Map<Long, ?> shortestPathTopo =
+		    topoRouteService.prepareShortestPathTopo();
 		Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
 		for (IFlowPath flowPathObj : allFlowPaths) {
 		    counterAllFlowPaths++;
@@ -352,7 +355,8 @@
 		    // to avoid closing the transaction.
 		    //
 		    DataPath dataPath =
-			topoRouteService.getTopoShortestPath(srcSwitchPort,
+			topoRouteService.getTopoShortestPath(shortestPathTopo,
+							     srcSwitchPort,
 							     dstSwitchPort);
 		    if (dataPath == null) {
 			// We need the DataPath to compare the paths
@@ -377,7 +381,7 @@
 		    conn.utils().removeFlowPath(conn, flowPathObj);
 		}
 
-		topoRouteService.dropShortestPathTopo();
+		topoRouteService.dropShortestPathTopo(shortestPathTopo);
 
 		conn.endTx(Transaction.COMMIT);
 
@@ -812,7 +816,7 @@
 	    thread.start();
 	}
 
-	// Want for all threads to complete
+	// Wait for all threads to complete
 	for (Thread thread : threads) {
 	    try {
 		thread.join();
@@ -1891,13 +1895,14 @@
 	// Prepare the Shortest Path computation if the first Flow Path
 	//
 	if (measurementStoredPaths.isEmpty())
-	    topoRouteService.prepareShortestPathTopo();
+	    measurementShortestPathTopo = topoRouteService.prepareShortestPathTopo();
 
 	//
 	// Compute the Shortest Path
 	//
 	DataPath dataPath =
-	    topoRouteService.getTopoShortestPath(flowPath.dataPath().srcPort(),
+	    topoRouteService.getTopoShortestPath(measurementShortestPathTopo,
+						 flowPath.dataPath().srcPort(),
 						 flowPath.dataPath().dstPort());
 	if (dataPath == null) {
 	    // We need the DataPath to populate the Network MAP
@@ -1956,12 +1961,39 @@
      */
     @Override
     public boolean measurementInstallPaths(Integer numThreads) {
-	List<Thread> threads = new LinkedList<Thread>();
-
 	// Create a copy of the Flow Paths to install
 	final ConcurrentLinkedQueue<FlowPath> measurementProcessingPaths =
 	    new ConcurrentLinkedQueue<FlowPath>(measurementStoredPaths);
 
+	/**
+	 * A Thread-wrapper class for executing the threads and collecting
+	 * the measurement data.
+	 */
+	class MyThread extends Thread {
+	    public long[] execTime = new long[2000];
+	    public int samples = 0;
+	    public int threadId = -1;
+	    @Override
+	    public void run() {
+		while (true) {
+		    FlowPath flowPath = measurementProcessingPaths.poll();
+		    if (flowPath == null)
+			return;
+		    // Install the Flow Path
+		    FlowId flowId = new FlowId();
+		    String dataPathSummaryStr =
+			flowPath.dataPath().dataPathSummary();
+		    long startTime = System.nanoTime();
+		    addFlow(flowPath, flowId, dataPathSummaryStr);
+		    long endTime = System.nanoTime();
+		    execTime[samples] = endTime - startTime;
+		    samples++;
+		}
+	    }
+	};
+
+	List<MyThread> threads = new LinkedList<MyThread>();
+
 	log.debug("Measurement Installing {} flows",
 		  measurementProcessingPaths.size());
 
@@ -1969,20 +2001,8 @@
 	// Create the threads to install the Flow Paths
 	//
 	for (int i = 0; i < numThreads; i++) {
-	    Thread thread = new Thread(new Runnable() {
-		@Override
-		public void run() {
-		    while (true) {
-			FlowPath flowPath = measurementProcessingPaths.poll();
-			if (flowPath == null)
-			    return;
-			// Install the Flow Path
-			FlowId flowId = new FlowId();
-			String dataPathSummaryStr =
-			    flowPath.dataPath().dataPathSummary();
-			addFlow(flowPath, flowId, dataPathSummaryStr);
-		    }
-		}}, "Measurement Add Flow Path");
+	    MyThread thread = new MyThread();
+	    thread.threadId = i;
 	    threads.add(thread);
 	}
 
@@ -1995,7 +2015,7 @@
 	    thread.start();
 	}
 
-	// Want for all threads to complete
+	// Wait for all threads to complete
 	for (Thread thread : threads) {
 	    try {
 		thread.join();
@@ -2007,6 +2027,20 @@
 	// Record the end of processing
 	measurementEndTimeProcessingPaths = System.nanoTime();
 
+	//
+	// Prepare the string with measurement data per each Flow Path
+	// installation.
+	// The string is multiple lines: one line per Flow Path installation:
+	//    ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
+	//
+	measurementPerFlowStr = new String();
+	String eol = System.getProperty("line.separator");
+	for (MyThread thread : threads) {
+	    for (int i = 0; i < thread.samples; i++) {
+		measurementPerFlowStr += "ThreadAndTimePerFlow " + thread.threadId + " " + numThreads + " " + thread.execTime[i] + eol;
+	    }
+	}
+
 	return true;
     }
 
@@ -2023,6 +2057,17 @@
     }
 
     /**
+     * Get the measurement install time per Flow.
+     *
+     * @return a multi-line string with the following format per line:
+     * ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
+     */
+    @Override
+    public String measurementGetPerFlowInstallTime() {
+	return new String(measurementPerFlowStr);
+    }
+
+    /**
      * Clear the path flows stored for measurement purpose.
      *
      * @return true on success, otherwise false.
@@ -2030,9 +2075,10 @@
     @Override
     public boolean measurementClearAllPaths() {
 	measurementStoredPaths.clear();
-	topoRouteService.dropShortestPathTopo();
+	topoRouteService.dropShortestPathTopo(measurementShortestPathTopo);
 	measurementStartTimeProcessingPaths = 0;
 	measurementEndTimeProcessingPaths = 0;
+	measurementPerFlowStr = new String();
 
 	return true;
     }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index 5e0db35..6c19fd0 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -145,6 +145,14 @@
     public Long measurementGetInstallPathsTimeNsec();
 
     /**
+     * Get the measurement install time per Flow.
+     *
+     * @return a multi-line string with the following format per line:
+     * ThreadAndTimePerFlow <ThreadId> <TotalThreads> <Time(ns)>
+     */
+    public String measurementGetPerFlowInstallTime();
+
+    /**
      * Clear the path flows stored for measurement purpose.
      *
      * @return true on success, otherwise false.
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
index 9fa5e63..fd9a319 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
@@ -25,6 +25,7 @@
         router.attach("/measurement-store-path/json", MeasurementStorePathFlowResource.class);
         router.attach("/measurement-install-paths/{num-threads}/json", MeasurementInstallPathsFlowResource.class);
         router.attach("/measurement-get-install-paths-time-nsec/json", MeasurementGetInstallPathsTimeNsecFlowResource.class);
+        router.attach("/measurement-get-per-flow-install-time/json", MeasurementGetPerFlowInstallTimeFlowResource.class);
         router.attach("/measurement-clear-all-paths/json", MeasurementClearAllPathsFlowResource.class);
         return router;
     }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetPerFlowInstallTimeFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetPerFlowInstallTimeFlowResource.java
new file mode 100644
index 0000000..adaecc8
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetPerFlowInstallTimeFlowResource.java
@@ -0,0 +1,37 @@
+package net.floodlightcontroller.flowcache.web;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowId;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MeasurementGetPerFlowInstallTimeFlowResource extends ServerResource {
+    protected static Logger log = LoggerFactory.getLogger(MeasurementGetPerFlowInstallTimeFlowResource.class);
+
+    @Get("json")
+    public String retrieve() {
+	String result = null;
+
+        IFlowService flowService =
+                (IFlowService)getContext().getAttributes().
+                get(IFlowService.class.getCanonicalName());
+
+        if (flowService == null) {
+	    log.debug("ONOS Flow Service not found");
+	    return result;
+	}
+
+	// Extract the arguments
+
+	// Process the request
+	result = flowService.measurementGetPerFlowInstallTime();
+
+	log.debug("Measurement Get Install Paths Time (nsec): " + result);
+
+	return result;
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index 634f7eb..72b2988 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -2175,4 +2175,16 @@
     public void setAutoPortFastFeature(boolean autoPortFastFeature) {
         this.autoPortFastFeature = autoPortFastFeature;
     }
+
+	@Override
+	public void switchPortAdded(Long switchId, OFPhysicalPort port) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	@Override
+	public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
+		// TODO Auto-generated method stub
+		
+	}
 }
diff --git a/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java b/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
index 723fe1c..d35a4f8 100644
--- a/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
+++ b/src/main/java/net/floodlightcontroller/onoslistener/OnosPublisher.java
@@ -6,12 +6,14 @@
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
+import org.openflow.protocol.OFPhysicalPort;
 import org.openflow.util.HexString;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.INetMapStorage.DM_OPERATION;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
 import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
 import net.floodlightcontroller.core.ISwitchStorage.SwitchState;
 import net.floodlightcontroller.core.IOFSwitch;
@@ -50,6 +52,7 @@
 	protected static final String DBConfigFile = "dbconf";
 	protected static final String CleanupEnabled = "EnableCleanup";
 	protected IThreadPoolService threadPool;
+	protected IFloodlightProviderService floodlightProvider;
 	
 	protected final int CLEANUP_TASK_INTERVAL = 60; // 1 min
 	protected SingletonTask cleanupTask;
@@ -116,12 +119,29 @@
 	@Override
 	public void linkDiscoveryUpdate(LDUpdate update) {
 		// TODO Auto-generated method stub
+		switch (update.getOperation()) {
+		
+			case LINK_REMOVED:
+				// TODO: Move network map link removal here
+				// reconcile paths here
+//				IPortObject srcPort = conn.utils().searchPort(conn, HexString.toHexString(update.getSrc()), update.getSrcPort());
+				break;
+					
+			default:
+				break;
+		}
 
 	}
 
 	@Override
 	public void addedSwitch(IOFSwitch sw) {
-		// TODO Auto-generated method stub
+
+		if (registryService.hasControl(sw.getId())) {
+	        	swStore.update(sw.getStringId(), SwitchState.ACTIVE, DM_OPERATION.UPDATE);
+	        	for (OFPhysicalPort port: sw.getPorts()) {
+	        		swStore.addPort(sw.getStringId(), port);
+	        	}
+		}
 
 	}
 
@@ -137,6 +157,19 @@
 
 	}
 
+
+	@Override
+	public void switchPortAdded(Long switchId, OFPhysicalPort port) {
+		// TODO Auto-generated method stub
+		swStore.addPort(HexString.toHexString(switchId), port);
+	}
+
+	@Override
+	public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
+		// TODO Auto-generated method stub
+		swStore.deletePort(HexString.toHexString(switchId), port.getPortNumber());		
+	}
+
 	@Override
 	public String getName() {
 		return "OnosPublisher";
@@ -206,6 +239,8 @@
 		conn = GraphDBConnection.getInstance(conf);
 		
 		log = LoggerFactory.getLogger(OnosPublisher.class);
+		floodlightProvider =
+	            context.getServiceImpl(IFloodlightProviderService.class);
 		deviceService = context.getServiceImpl(IDeviceService.class);
 		threadPool = context.getServiceImpl(IThreadPoolService.class);
 		registryService = context.getServiceImpl(IControllerRegistryService.class);
@@ -227,6 +262,7 @@
 		String cleanupNeeded = configMap.get(CleanupEnabled);
 
 		deviceService.addListener(this);
+		floodlightProvider.addOFSwitchListener(this);
 		
 		log.debug("Adding EventListener");
 		conn.addEventListener(new LocalTopologyEventListener(conn));
diff --git a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
index 95a3b19..1e002aa 100644
--- a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
+++ b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
@@ -101,14 +101,6 @@
     
     GraphDBConnection conn;
 
-    //
-    // Topology state for storing (on demand) Switch and Ports info for
-    // fast access during the shortest path computation.
-    // It is explicitly populated by method @ref prepareShortestPathTopo().
-    // See the documentation for that method for details.
-    //
-    HashMap<Long, Node> shortestPathTopo;
-
     @Override
     public Collection<Class<? extends IFloodlightService>> getModuleServices() {
         Collection<Class<? extends IFloodlightService>> l = 
@@ -168,7 +160,7 @@
 
     /**
      * Fetch the Switch and Ports info from the Titan Graph
-     * and store it locally for fast access during the shortest path
+     * and return it for fast access during the shortest path
      * computation.
      *
      * After fetching the state, method @ref getTopoShortestPath()
@@ -184,16 +176,18 @@
      * method @ref dropShortestPathTopo() should be used to release
      * the internal state that is not needed anymore:
      *
-     *       prepareShortestPathTopo();
+     *       Map<Long, ?> shortestPathTopo;
+     *       shortestPathTopo = prepareShortestPathTopo();
      *       for (int i = 0; i < 10000; i++) {
-     *           dataPath = getTopoShortestPath(...);
+     *           dataPath = getTopoShortestPath(shortestPathTopo, ...);
      *           ...
      *        }
-     *        dropShortestPathTopo();
+     *        dropShortestPathTopo(shortestPathTopo);
+     *
+     * @return the Shortest Path info handler stored in a map.
      */
-    
-    public void prepareShortestPathTopo() {
-	shortestPathTopo = new HashMap<Long, Node>();
+    public Map<Long, ?> prepareShortestPathTopo() {
+	Map<Long, Node> shortestPathTopo = new HashMap<Long, Node>();
 
 	//
 	// Fetch the relevant info from the Switch and Port vertices
@@ -260,6 +254,8 @@
 	    }
 	}
 	conn.endTx(Transaction.COMMIT);
+
+	return shortestPathTopo;
     }
 
     /**
@@ -268,9 +264,10 @@
      *
      * See the documentation for method @ref prepareShortestPathTopo()
      * for additional information and usage.
+     *
+     * @shortestPathTopo the Shortest Path info handler to release.
      */
-  
-    public void dropShortestPathTopo() {
+    public void dropShortestPathTopo(Map<Long, ?> shortestPathTopo) {
 	shortestPathTopo = null;
     }
 
@@ -282,13 +279,17 @@
      * See the documentation for method @ref prepareShortestPathTopo()
      * for additional information and usage.
      *
+     * @param shortestPathTopoHandler the Shortest Path info handler
+     * to use.
      * @param src the source in the shortest path computation.
      * @param dest the destination in the shortest path computation.
      * @return the data path with the computed shortest path if
      * found, otherwise null.
      */
-  
-    public DataPath getTopoShortestPath(SwitchPort src, SwitchPort dest) {
+    public DataPath getTopoShortestPath(Map<Long, ?> shortestPathTopoHandler,
+					SwitchPort src, SwitchPort dest) {
+	@SuppressWarnings("unchecked")
+	Map<Long, Node> shortestPathTopo = (Map)shortestPathTopoHandler;
 	DataPath result_data_path = new DataPath();
 
 	// Initialize the source and destination in the data path to return
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
index 4ed59d7..371a479 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
+++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
@@ -39,6 +39,7 @@
 import org.openflow.protocol.OFFlowRemoved;
 import org.openflow.protocol.OFMatch;
 import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPhysicalPort;
 import org.openflow.protocol.OFType;
 import org.openflow.protocol.factory.BasicFactory;
 import org.openflow.util.HexString;
@@ -675,5 +676,17 @@
             Map<String, String> removedControllerNodeIPs) {
         // ignore
     }
+
+	@Override
+	public void switchPortAdded(Long switchId, OFPhysicalPort port) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	@Override
+	public void switchPortRemoved(Long switchId, OFPhysicalPort port) {
+		// TODO Auto-generated method stub
+		
+	}
      
 }
diff --git a/web/measurement_get_per_flow_install_time.py b/web/measurement_get_per_flow_install_time.py
new file mode 100755
index 0000000..bf2bcc7
--- /dev/null
+++ b/web/measurement_get_per_flow_install_time.py
@@ -0,0 +1,61 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import pprint
+import os
+import sys
+import subprocess
+import json
+import argparse
+import io
+import time
+
+from flask import Flask, json, Response, render_template, make_response, request
+
+#
+# TODO: remove this! We don't use JSON argument here!
+# curl http://127.0.0.1:8080/wm/flow/delete/{"value":"0xf"}/json'
+#
+
+## Global Var ##
+ControllerIP="127.0.0.1"
+ControllerPort=8080
+
+DEBUG=0
+pp = pprint.PrettyPrinter(indent=4)
+
+app = Flask(__name__)
+
+## Worker Functions ##
+def log_error(txt):
+  print '%s' % (txt)
+
+def debug(txt):
+  if DEBUG:
+    print '%s' % (txt)
+
+# @app.route("/wm/flow/measurement-get-per-flow-install-time/json")
+def measurement_get_per_flow_install_time():
+  command = "curl -s \"http://%s:%s/wm/flow/measurement-get-per-flow-install-time/json\"" % (ControllerIP, ControllerPort)
+  debug("measurement_get_per_flow_install_time %s" % command)
+  result = os.popen(command).read()
+  print '%s' % (result)
+  # parsedResult = json.loads(result)
+  # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+  usage_msg = "Get the measured time per flow to install each stored flow path\n"
+  usage_msg = usage_msg + "Usage: %s\n" % (sys.argv[0])
+  usage_msg = usage_msg + "\n"
+
+  # app.debug = False;
+
+  # Usage info
+  if len(sys.argv) > 1 and (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
+    print(usage_msg)
+    exit(0)
+
+  # Check arguments
+
+  # Do the work
+  measurement_get_per_flow_install_time()
diff --git a/web/measurement_process.py b/web/measurement_process.py
new file mode 100755
index 0000000..3187299
--- /dev/null
+++ b/web/measurement_process.py
@@ -0,0 +1,59 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import functools
+import math
+import sys
+
+## {{{ http://code.activestate.com/recipes/511478/ (r1)
+
+def percentile(N, percent, key=lambda x:x):
+    """
+    Find the percentile of a list of values.
+
+    @parameter N - is a list of values. Note N MUST BE already sorted.
+    @parameter percent - a float value from 0.0 to 1.0.
+    @parameter key - optional key function to compute value from each element of N.
+
+    @return - the percentile of the values
+    """
+    if not N:
+        return None
+    k = (len(N)-1) * percent
+    f = math.floor(k)
+    c = math.ceil(k)
+    if f == c:
+        return key(N[int(k)])
+    d0 = key(N[int(f)]) * (c-k)
+    d1 = key(N[int(c)]) * (k-f)
+    return d0+d1
+
+# median is 50th percentile.
+# median = functools.partial(percentile, percent=0.5)
+## end of http://code.activestate.com/recipes/511478/ }}}
+
+if __name__ == "__main__":
+
+    dict = {}
+
+    #
+    # Read the data from the stdin, and store it in a dictionary.
+    # The dictionary uses lists as values.
+    #
+    data = sys.stdin.readlines()
+    for line in data:
+	words = line.split()
+	thread_n = int(words[0])
+	msec = float(words[1])
+	dict.setdefault(thread_n, []).append(msec)
+
+    #
+    # Compute and print the values: median (50-th), 10-th, and 90-th
+    # percentile:
+    # <key> <median> <10-percentile> <90-percentile>
+    #
+    for key, val_list in sorted(dict.items()):
+	val_10 = percentile(sorted(val_list), 0.1)
+	val_50 = percentile(sorted(val_list), 0.5)
+	val_90 = percentile(sorted(val_list), 0.9)
+	print "%s %s %s %s" % (str(key), str(val_50), str(val_10), str(val_90))
diff --git a/web/measurement_run.py b/web/measurement_run.py
new file mode 100755
index 0000000..80d0517
--- /dev/null
+++ b/web/measurement_run.py
@@ -0,0 +1,104 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import os
+import string
+import subprocess
+import time
+
+# flow_n = 252
+# threads_n = [1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100]
+# iterations_n = 10
+
+flow_n = 1
+threads_n = [1]
+iterations_n = 10
+# iterations_n = 100
+
+# flow_n = 42
+# flow_n = 420
+# flow_n = 1008
+
+def run_command(cmd):
+    """
+    - Run an external command, and return a tuple: stdout as the
+    first argument, and stderr as the second argument.
+    - Returns None if error.
+    """
+    try:
+	pr = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+	ret_tuple = pr.communicate();
+	if pr.returncode:
+	    print "%s failed with error code: %s" % (cmd, str(pr.returncode))
+	return ret_tuple
+    except OSError:
+	print "OS Error running %s" % cmd
+
+def run_install_paths(flowdef_filename):
+    # Prepare the flows to measure
+    cmd = "web/measurement_store_flow.py -f " + flowdef_filename
+    os.system(cmd)
+
+def run_measurement(thread_n):
+    # Install the Flow Paths
+    cmd = ["web/measurement_install_paths.py", str(thread_n)]
+    run_command(cmd)
+
+    # Get the measurement data and print it
+    cmd = "web/measurement_get_install_paths_time_nsec.py"
+    r = run_command(cmd)		# Tuple: [<stdout>, <stderr>]
+    res = r[0].split()			# Tuple: [<num>, nsec]
+    nsec_str = res[0]
+    msec = float(nsec_str) / (1000 * 1000)
+
+    # Get the measurement data and print it
+    cmd = "web/measurement_get_per_flow_install_time.py"
+    r = run_command(cmd)		# Tuple: [<stdout>, <stderr>]
+    res = r[0]
+    print res
+
+    # Keep checking until all Flow Paths are installed
+    while True:
+	# time.sleep(3)
+	cmd = ["web/get_flow.py", "all"]
+	r = run_command(cmd)
+	if string.count(r[0], "FlowPath") != flow_n:
+	    continue
+	if string.find(r[0], "NOT") == -1:
+	    break
+
+    # Remove the installed Flow Paths
+    cmd = ["web/delete_flow.py", "all"]
+    run_command(cmd)
+
+    # Keep checking until all Flows are removed
+    while True:
+	# time.sleep(3)
+	cmd = ["web/get_flow.py", "all"]
+	r = run_command(cmd)
+	if r[0] == "":
+	    break
+
+    return msec
+
+
+if __name__ == "__main__":
+
+    # Initial cleanup
+    cmd = "web/measurement_clear_all_paths.py"
+    run_command(cmd)
+
+    # Install the Flow Paths to measure
+    flowdef_filename = "web/flowdef_8node_" + str(flow_n) + ".txt"
+    run_install_paths(flowdef_filename)
+
+    # Do the work
+    for thread_n in threads_n:
+	for n in range(iterations_n):
+	    msec = run_measurement(thread_n)
+	    # Format: <number of threads> <time in ms>
+	    print "%d %f" % (thread_n, msec / flow_n)
+
+    # Cleanup on exit
+    cmd = "web/measurement_clear_all_paths.py"
+    run_command(cmd)