Added initial implementation (untested) of code (controlled by REST API)
for measuring the performance of writing Flow state to the Network MAP.

Currently, the semantics are:
1. Call multiple times the following REST call to store internally
   each flow:
   http://localhost:8080/wm/flow/measurement-store-path/json

   Each call is similar to the "add-shortest-path" REST call
   when adding a regular flow: the JSON object contains the endpoints
   and the match conditions.

   The "measurement-store-path" Flow is stored internally and will
   be used later for measurement purpose.

2. Use the following REST call to start the measurements:

   http://localhost:8080/wm/flow/measurement-install-paths/{num-threads}/json

   The "num-threads" argument is used to specify how many threads should
   be used to install the measurement flows.

3. Use the following REST call to obtain how long time (in nanoseconds)
   it took to install the measurement flows in the Network MAP:

   http://localhost:8080/wm/flow/measurement-get-install-paths-time-nsec/json

4. Use the following REST call to clear the measurement flows that
   were stored internally in Step 1:

   http://localhost:8080/wm/flow/measurement-clear-all-paths/json

Note that in Step 4 the installed Flows are NOT deleted from the
Network MAP.

NOTE: Step (2) and (3) can be repeated multiple times (e.g., for a different
number of theads) for the same set of Flows that were stored in Step (1).
However, it might be desirable that Step (3) is followed by an explicit
deletion of the installed Flows from the Network MAP (and the switches).
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 307919a..7efa49c 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -87,9 +87,15 @@
     private static int nextFlowEntryIdSuffix = 0;
     private static long nextFlowEntryId = 0;
 
+    // State for measurement purpose
     private static long measurementFlowId = 100000;
     private static String measurementFlowIdStr = "0x186a0";	// 100000
     private long modifiedMeasurementFlowTime = 0;
+    //
+    private LinkedList<FlowPath> measurementStoredPaths = new LinkedList<FlowPath>();
+    private LinkedList<FlowPath> measurementProcessingPaths = null;
+    private long measurementStartTimeProcessingPaths = 0;
+    private long measurementEndTimeProcessingPaths = 0;
 
     /** The logger. */
     private static Logger log = LoggerFactory.getLogger(FlowManager.class);
@@ -462,7 +468,7 @@
 		shortestPathReconcileScheduler = Executors.newScheduledThreadPool(1);
     }
 
-    private long getNextFlowEntryId() {
+    private synchronized long getNextFlowEntryId() {
 	//
 	// Generate the next Flow Entry ID.
 	// NOTE: For now, the higher 32 bits are random, and
@@ -1763,4 +1769,174 @@
 	//
 	return (installRemoteFlowEntry(flowPath, flowEntry));
     }
+
+    /**
+     * Store a path flow for measurement purpose.
+     *
+     * NOTE: The Flow Path argument does NOT contain flow entries.
+     * The Shortest Path is computed, and the corresponding Flow Entries
+     * are stored in the Flow Path.
+     *
+     * @param flowPath the Flow Path with the endpoints and the match
+     * conditions to store.
+     * @return the stored shortest-path flow on success, otherwise null.
+     */
+    @Override
+    public FlowPath measurementStorePathFlow(FlowPath flowPath) {
+	//
+	// Prepare the Shortest Path computation if the first Flow Path
+	//
+	if (measurementStoredPaths.isEmpty())
+	    topoRouteService.prepareShortestPathTopo();
+
+	//
+	// Compute the Shortest Path
+	//
+	DataPath dataPath =
+	    topoRouteService.getTopoShortestPath(flowPath.dataPath().srcPort(),
+						 flowPath.dataPath().dstPort());
+	if (dataPath == null) {
+	    // We need the DataPath to populate the Network MAP
+	    dataPath = new DataPath();
+	    dataPath.setSrcPort(flowPath.dataPath().srcPort());
+	    dataPath.setDstPort(flowPath.dataPath().dstPort());
+	}
+
+	//
+	// Set the incoming port matching and the outgoing port output
+	// actions for each flow entry.
+	//
+	for (FlowEntry flowEntry : dataPath.flowEntries()) {
+	    // Set the incoming port matching
+	    FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
+	    flowEntry.setFlowEntryMatch(flowEntryMatch);
+	    flowEntryMatch.enableInPort(flowEntry.inPort());
+
+	    // Set the outgoing port output action
+	    ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
+	    if (flowEntryActions == null) {
+		flowEntryActions = new ArrayList<FlowEntryAction>();
+		flowEntry.setFlowEntryActions(flowEntryActions);
+	    }
+	    FlowEntryAction flowEntryAction = new FlowEntryAction();
+	    flowEntryAction.setActionOutput(flowEntry.outPort());
+	    flowEntryActions.add(flowEntryAction);
+	}
+
+	//
+	// Prepare the computed Flow Path
+	//
+	FlowPath computedFlowPath = new FlowPath();
+	computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
+	computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
+	computedFlowPath.setDataPath(dataPath);
+	computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
+
+	//
+	// Add the computed Flow Path the the internal storage
+	//
+	measurementStoredPaths.add(computedFlowPath);
+
+	return (computedFlowPath);
+    }
+
+    /**
+     * Install path flows for measurement purpose.
+     *
+     * @param numThreads the number of threads to use to install the path
+     * flows.
+     * @return true on success, otherwise false.
+     */
+    @Override
+    public boolean measurementInstallPaths(Integer numThreads) {
+	List<Thread> threads = new LinkedList<Thread>();
+
+	measurementProcessingPaths = measurementStoredPaths;
+	//
+	// 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 = measurementPollFirstFlowPath();
+			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");
+	    threads.add(thread);
+	}
+
+	//
+	// Start processing
+	//
+	measurementEndTimeProcessingPaths = 0;
+	measurementStartTimeProcessingPaths = System.nanoTime();
+	for (Thread thread : threads) {
+	    thread.start();
+	}
+
+	//
+	// Wait until the end of processing time
+	//
+	while (measurementEndTimeProcessingPaths == 0) {
+	    try {
+		Thread.sleep(100);
+	    } catch (InterruptedException e) {
+		// Continue waiting
+	    }
+	}
+
+	return true;
+    }
+
+    /**
+     * Get the measurement time that took to install the path flows.
+     *
+     * @return the measurement time (in nanoseconds) it took to install
+     * the path flows.
+     */
+    @Override
+    public Long measurementGetInstallPathsTimeNsec() {
+	return new Long(measurementEndTimeProcessingPaths -
+			measurementStartTimeProcessingPaths);
+    }
+
+    /**
+     * Get a Flow Path that needs to be installed for measurement purpose.
+     *
+     * If there is no next Flow Path to install, the end time measurement
+     * is recorded.
+     *
+     * @return the next Flow Path to install if exists, otherwise null.
+     */
+    private synchronized FlowPath measurementPollFirstFlowPath() {
+	FlowPath flowPath = measurementProcessingPaths.pollFirst();
+
+	// Record the end of processing, if the first call
+	if ((flowPath == null) && (measurementEndTimeProcessingPaths == 0))
+	    measurementEndTimeProcessingPaths = System.nanoTime();
+
+	return flowPath;
+    }
+
+    /**
+     * Clear the path flows stored for measurement purpose.
+     *
+     * @return true on success, otherwise false.
+     */
+    @Override
+    public boolean measurementClearAllPaths() {
+	measurementStoredPaths.clear();
+	topoRouteService.dropShortestPathTopo();
+
+	return true;
+    }
+
 }