Merge branch 'master' of github.com:OPENNETWORKINGLAB/ONOS
diff --git a/.gitignore b/.gitignore
index 3c5748b..f8dcd13 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
 *.class
 .classpath
 .project
+.pydevproject
 target
 onos-logs
 onos.log
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..0d03e90
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,289 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <prerequisites>
+    <maven>3.0.4</maven>
+  </prerequisites>
+  <groupId>net.onrc.onos</groupId>
+  <artifactId>ONOS</artifactId>
+  <version>0.1.0</version>
+  <packaging>jar</packaging>
+  <name>ONOS</name>
+  <url>http://onlab.us/</url>
+  <repositories>
+    <repository>
+      <id>central</id>
+      <name>Maven Central repository</name>
+      <url>http://repo1.maven.org/maven2</url>
+    </repository>
+    <repository>
+      <id>maven-restlet</id>
+      <name>Public online Restlet repository</name>
+      <url>http://maven.restlet.org</url>
+    </repository>
+    <repository>
+      <id>tinkerpop-repository</id>
+      <name>TinkerPop Maven2 Repository</name>
+      <url>http://tinkerpop.com/maven2</url>
+    </repository>
+  </repositories>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-install-plugin</artifactId>
+        <version>2.3.1</version>
+        <executions>
+        </executions>
+      </plugin>
+      <!-- guice maven plugin for dependency injection inside maven -->
+      <plugin>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>guice-maven-plugin</artifactId>
+        <version>2.11.0</version>
+      </plugin>
+      <!-- compile -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>2.3.2</version>
+        <configuration>
+          <source>1.6</source>
+          <target>1.6</target>
+          <encoding>UTF-8</encoding>
+        </configuration>
+        <executions>                         
+         </executions>
+      </plugin>
+      <!-- test -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.12</version>
+        <configuration>
+          <excludes>
+            <!-- exclude all test cases for now -->
+            <!-- <exclude>**/storage/tests/StorageTest.java</exclude> -->
+            <!-- <exclude>**/test/*</exclude> -->
+            <exclude>**/test/*</exclude>
+            <exclude>**/Test*.java</exclude>
+            <exclude>**/*Test.java</exclude>
+            <exclude>**/*TestCase.java</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+      <!-- exec:java -->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <version>1.2.1</version>
+        <configuration>
+          <mainClass>net.floodlightcontroller.core.Main</mainClass>
+        </configuration>
+        <executions>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.7</version>
+        <executions>
+          <execution>
+            <id>add-source</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>add-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>lib/gen-java</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <!-- for getting visualization reporting   -->
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>guice-maven-plugin</artifactId>
+        <version>2.11.0</version>
+      </plugin>
+    </plugins>
+  </reporting>
+  <dependencies>
+    <dependency>
+      <groupId>asm</groupId>
+      <artifactId>asm-tree</artifactId>
+      <version>3.0</version>
+    </dependency>
+    <dependency>
+      <groupId>net.sourceforge.cobertura</groupId>
+      <artifactId>cobertura</artifactId>
+      <version>1.9.4.1</version>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-core</artifactId>
+      <version>1.0.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.objenesis</groupId>
+      <artifactId>objenesis</artifactId>
+      <version>1.2</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>13.0.1</version>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <version>1.0.0</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-core-asl</artifactId>
+      <version>1.9.11</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-mapper-asl</artifactId>
+      <version>1.9.11</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.6.4</version>
+    </dependency>
+    <dependency>
+      <groupId>org.restlet.jse</groupId>
+      <artifactId>org.restlet</artifactId>
+      <version>2.1-RC1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.restlet.jse</groupId>
+      <artifactId>org.restlet.ext.jackson</artifactId>
+      <version>2.1-RC1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.restlet.jse</groupId>
+      <artifactId>org.restlet.ext.simple</artifactId>
+      <version>2.1-RC1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.restlet.jse</groupId>
+      <artifactId>org.restlet.ext.slf4j</artifactId>
+      <version>2.1-RC1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.simpleframework</groupId>
+      <artifactId>simple</artifactId>
+      <version>4.1.21</version>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.netty</groupId>
+      <artifactId>netty</artifactId>
+      <version>3.2.6.Final</version>
+    </dependency>
+    <dependency>
+      <groupId>args4j</groupId>
+      <artifactId>args4j</artifactId>
+      <version>2.0.16</version>
+    </dependency>
+    <dependency>
+      <groupId>com.googlecode.concurrentlinkedhashmap</groupId>
+      <artifactId>concurrentlinkedhashmap-lru</artifactId>
+      <version>1.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.python</groupId>
+      <artifactId>jython-standalone</artifactId>
+      <version>2.5.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.thrift</groupId>
+      <artifactId>libthrift</artifactId>
+      <version>0.7.0</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.8.2</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <version>3.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>cglib</groupId>
+      <artifactId>cglib-nodep</artifactId>
+      <version>2.2.2</version>
+    </dependency>
+    <dependency>
+      <groupId>com.thinkaurelius.titan</groupId>
+      <artifactId>titan-all</artifactId>
+      <version>0.2.1</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+      <version>3.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.tinkerpop</groupId>
+      <artifactId>frames</artifactId>
+      <version>2.3.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.tinkerpop.blueprints</groupId>
+      <artifactId>blueprints-core</artifactId>
+      <version>2.3.0</version>
+    </dependency>
+    <!-- dependency to locally modified version -->
+    <dependency>
+      <groupId>com.netflix.curator</groupId>
+      <artifactId>curator-framework</artifactId>
+      <version>1.3.5-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>com.netflix.curator</groupId>
+      <artifactId>curator-client</artifactId>
+      <version>1.3.5-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>com.netflix.curator</groupId>
+      <artifactId>curator-recipes</artifactId>
+      <version>1.3.5-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>com.netflix.curator</groupId>
+      <artifactId>curator-x-discovery</artifactId>
+      <version>1.3.5-SNAPSHOT</version>
+    </dependency>
+    <!--
+    <dependency>
+      <groupId>net.floodlightcontroller</groupId>
+      <artifactId>packetstreamer-thrift</artifactId>
+      <version>0.1.0</version>
+    </dependency>
+    -->
+    <dependency>
+      <groupId>net.sf.json-lib</groupId>
+      <artifactId>json-lib</artifactId>
+      <version>2.4</version>
+      <classifier>jdk15</classifier>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/scripts/all-linkup-hw.sh b/scripts/all-linkup-hw.sh
new file mode 100755
index 0000000..41d6be8
--- /dev/null
+++ b/scripts/all-linkup-hw.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#   BRINGS BACK UP ALL THE LINKS FOR THE HARDWARE DEMO AS SHOWN IN ONS 2013
+#   link-hw.sh %s %s %s  % (src_dpid, port1, cmd)
+
+./link-hw.sh 00:00:00:00:ba:5e:ba:11 24 up 
+./link-hw.sh 00:00:00:00:ba:5e:ba:11 23 up 
+
+./link-hw.sh 00:00:00:00:ba:5e:ba:13 22 up
+./link-hw.sh 00:00:00:00:ba:5e:ba:13 23 up
+
+./link-hw.sh 00:00:00:00:00:00:ba:12 23 up
+./link-hw.sh 00:00:00:00:00:00:ba:12 22 up
+./link-hw.sh 00:00:00:00:00:00:ba:12 24 up
+
+./link-hw.sh 00:01:00:16:97:08:9a:46 23 up
+./link-hw.sh 00:01:00:16:97:08:9a:46 24 up
+
+./link-hw.sh 00:00:20:4e:7f:51:8a:35 21 up 
+./link-hw.sh 00:00:20:4e:7f:51:8a:35 22 up 
+./link-hw.sh 00:00:20:4e:7f:51:8a:35 24 up 
+./link-hw.sh 00:00:20:4e:7f:51:8a:35 23 up 
diff --git a/scripts/neclink.exp b/scripts/neclink.exp
index a09ed11..0815843 100755
--- a/scripts/neclink.exp
+++ b/scripts/neclink.exp
@@ -1,6 +1,6 @@
 #!/usr/bin/expect -f
 # ./neclink.exp <ip of port> <'no' or blank>
-set timeout -1
+set timeout 5
 set port [lindex $argv 0]
 set no [lindex $argv 1]
 
diff --git a/scripts/prontolink.exp b/scripts/prontolink.exp
index 7786701..501cccd 100755
--- a/scripts/prontolink.exp
+++ b/scripts/prontolink.exp
@@ -1,6 +1,6 @@
 #!/usr/bin/expect -f
 # ./prontolink.exp <ip of switch> <port> <0 or 1 (on or off)>
-set timeout -1
+set timeout 5
 set arg0 [lindex $argv 0]
 set port [lindex $argv 1]
 set onoff [lindex $argv 2]
diff --git a/scripts/run-onos-simple.sh b/scripts/run-onos-simple.sh
deleted file mode 100755
index 41b2cc6..0000000
--- a/scripts/run-onos-simple.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-basedir=`dirname $0`
-onosdir="${basedir}/.."
-
-logfile="${onosdir}/logback.xml"
-jarfile="${onosdir}/target/floodlight-only.jar"
-classpath="${jarfile}:${onosdir}/lib/*:${onosdir}/lib/titan/*"
-mainclass="net.floodlightcontroller.core.Main"
-propfile="${onosdir}/onos.properties"
-
-#java -Dlogback.configurationFile=logback.xml -cp target/floodlight-only.jar:lib/*:lib/titan/* net.floodlightcontroller.core.Main -cf onos.properties
-java -Dlogback.configurationFile=${logfile} -cp ${classpath} ${mainclass} -cf ${propfile}
diff --git a/scripts/runiperf.sh b/scripts/runiperf.sh
index 7066bc6..4e26bd2 100755
--- a/scripts/runiperf.sh
+++ b/scripts/runiperf.sh
@@ -45,7 +45,7 @@
       if (testbed == "SW"): 
         cmd="ssh -o StrictHostKeyChecking=no 1.1.%d.1 '/home/ubuntu/ONOS/scripts/iperf -u -t%s -i%s -k%s -yJ -o /home/ubuntu/ONOS/web/log/iperfclient_%s.out -c 192.168.%d.%d 2>&1 &' &" % (src_hostid, duration, interval, samples, flowid, dst_nwid, dst_hostid)
       else:
-        cmd="~/mininet/util/m g%sh%02d '/home/ubuntu/ONOS/scripts/iperf -u -t%s -i%s -k%s -yJ -o /home/ubuntu/ONOS/web/log/iperfclient_%s.out -c 192.168.%d.%d 2>&1 &' &" % (src_nwid, src_hostid, duration, interval, samples, flowid, dst_nwid, dst_hostid + 1)
+        cmd="~/mininet/util/m g%sh%02d '/home/ubuntu/ONOS/scripts/iperf -u -t%s -i%s -k%s -yJ -o /home/ubuntu/ONOS/web/log/iperfclient_%s.out -c 192.168.%d.%d 2>&1 &' &" % (src_nwid, src_hostid, duration, interval, samples, flowid, dst_nwid, dst_hostid )
       killcmd='sudo pkill -KILL -f \"iperf .* -o .*/iperfclient_%s.out\"' % (flowid)
       print killcmd
       print cmd
diff --git a/setup-eclipse.sh b/setup-eclipse.sh
index a39dc62..f88377e 100755
--- a/setup-eclipse.sh
+++ b/setup-eclipse.sh
@@ -1,50 +1,4 @@
 #!/bin/bash
 
-d=$(dirname $0)
-MAIN_CLASS=$1
-LIBRARIES=$2
-[ "${MAIN_CLASS}" ] || { echo "Run 'ant eclipse' to generate Eclipse project files"; exit 1; }
+mvn eclipse:eclipse
 
-
-cat >$d/.project <<EOF
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>floodlight</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
-EOF
-
-
-cat >$d/.classpath <<EOF
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/main/java" output="target/bin"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-        <classpathentry kind="src" path="src/test/java" output="target/bin-test"/>
-        <classpathentry kind="src" path="lib/gen-java" output="target/bin"/>
-EOF
-(
-IFS=":"
-for l in ${LIBRARIES}; do
-cat >>$d/.classpath <<EOF
-	<classpathentry exported="true" kind="lib" path="$l"/>
-EOF
-done
-)
-cat >>$d/.classpath <<EOF
-	<classpathentry exported="true" kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="output" path="target/bin"/>
-</classpath>
-EOF
diff --git a/setup-local-jar.sh b/setup-local-jar.sh
new file mode 100755
index 0000000..1f72c44
--- /dev/null
+++ b/setup-local-jar.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+ mvn install:install-file -Dfile=./lib/curator-framework-1.3.5-SNAPSHOT.jar -DgroupId=com.netflix.curator -DartifactId=curator-framework -Dversion=1.3.5-SNAPSHOT -Dpackaging=jar -DgeneratePom=true
+ mvn install:install-file -Dfile=./lib/curator-client-1.3.5-SNAPSHOT.jar -DgroupId=com.netflix.curator -DartifactId=curator-client -Dversion=1.3.5-SNAPSHOT -Dpackaging=jar -DgeneratePom=true
+ mvn install:install-file -Dfile=./lib/curator-recipes-1.3.5-SNAPSHOT.jar -DgroupId=com.netflix.curator -DartifactId=curator-recipes -Dversion=1.3.5-SNAPSHOT -Dpackaging=jar -DgeneratePom=true
+ mvn install:install-file -Dfile=./lib/curator-x-discovery-1.3.5-SNAPSHOT.jar -DgroupId=com.netflix.curator -DartifactId=curator-x-discovery -Dversion=1.3.5-SNAPSHOT -Dpackaging=jar -DgeneratePom=true
+# mvn install:install-file -Dfile=./lib/packetstreamer-thrift-0.1.0.jar -DgroupId=net.floodlightcontroller -DartifactId=packetstreamer-thrift -Dversion=0.1.0 -Dpackaging=jar -DgeneratePom=true
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
index 47f3d1a..517126c 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
@@ -1,73 +1,144 @@
 package net.floodlightcontroller.bgproute;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Collection;
-import java.util.Map;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.INetMapTopologyService.ITopoRouteService;
+import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.core.IFloodlightProviderService;
-
+import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
+import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.topology.ITopologyListener;
 import net.floodlightcontroller.topology.ITopologyService;
-import net.floodlightcontroller.restclient.RestClient;
-
-import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
+import net.floodlightcontroller.util.DataPath;
+import net.floodlightcontroller.util.Dpid;
+import net.floodlightcontroller.util.FlowEntry;
+import net.floodlightcontroller.util.IPv4;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.Port;
+import net.floodlightcontroller.util.SwitchPort;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 import net.sf.json.JSONSerializer;
 
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionDataLayerDestination;
+import org.openflow.protocol.action.OFActionOutput;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.net.InetAddresses;
+
 public class BgpRoute implements IFloodlightModule, IBgpRouteService, ITopologyListener {
 	
 	protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
 
 	protected IFloodlightProviderService floodlightProvider;
 	protected ITopologyService topology;
+	protected ITopoRouteService topoRouteService;
+	protected IDeviceService devices;
+	protected IRestApiService restApi;
 	
 	protected static Ptree ptree;
-	protected static String BGPdRestIp;
-	protected static String RouterId;
+	protected String bgpdRestIp;
+	protected String routerId;
 	
+	protected Set<InetAddress> routerIpAddresses;
 	
+	//We need to identify our flows somehow. But like it says in LearningSwitch.java,
+	//the controller/OS should hand out cookie IDs to prevent conflicts.
+	protected final long APP_COOKIE = 0xa0000000000000L;
+	//Cookie for flows that do L2 forwarding within SDN domain to egress routers
+	protected final long L2_FWD_COOKIE = APP_COOKIE + 1;
+	//Cookie for flows in ingress switches that rewrite the MAC address
+	protected final long MAC_RW_COOKIE = APP_COOKIE + 2;
+	//Forwarding uses priority 0, and the mac rewrite entries in ingress switches
+	//need to be higher priority than this otherwise the rewrite may not get done
+	protected final short SDNIP_PRIORITY = 10;
+	
+	//TODO temporary
+	protected List<GatewayRouter> gatewayRouters;
+	
+	private void initGateways(){
+		gatewayRouters = new ArrayList<GatewayRouter>();
+		//00:00:00:00:00:00:0s0:a3 port 1
+		gatewayRouters.add(
+				new GatewayRouter(new SwitchPort(new Dpid(163L), new Port((short)1)),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x02, 0x01}),
+				new IPv4("192.168.10.1"),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}),
+				new IPv4("192.168.10.101")));
+		//00:00:00:00:00:00:00:a5 port 1
+		//gatewayRouters.add(new SwitchPort(new Dpid(165L), new Port((short)1)));
+		gatewayRouters.add(
+				new GatewayRouter(new SwitchPort(new Dpid(165L), new Port((short)1)),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x02, 0x02}),
+				new IPv4("192.168.20.1"),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}),
+				new IPv4("192.168.20.101")));
+		//00:00:00:00:00:00:00:a2 port 1
+		//gatewayRouters.add(new SwitchPort(new Dpid(162L), new Port((short)1)));
+		gatewayRouters.add(
+				new GatewayRouter(new SwitchPort(new Dpid(162L), new Port((short)1)),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x03, 0x01}),
+				new IPv4("192.168.30.1"),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}),
+				new IPv4("192.168.30.101")));
+		//00:00:00:00:00:00:00:a6
+		//gatewayRouters.add(new SwitchPort(new Dpid(166L), new Port((short)1)));
+		gatewayRouters.add(
+				new GatewayRouter(new SwitchPort(new Dpid(166L), new Port((short)1)),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x04, 0x01}),
+				new IPv4("192.168.40.1"),
+				new MACAddress(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}),
+				new IPv4("192.168.40.101")));
+		
+	}
 	
 	@Override
 	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
-		Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+		Collection<Class<? extends IFloodlightService>> l 
+			= new ArrayList<Class<? extends IFloodlightService>>();
 		l.add(IBgpRouteService.class);
 		return l;
 	}
 
 	@Override
 	public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
-		Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+		Map<Class<? extends IFloodlightService>, IFloodlightService> m 
+			= new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
 		m.put(IBgpRouteService.class, this);
 		return m;
 	}
 
-	protected IRestApiService restApi;
-	
 	@Override
 	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
-		Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+		Collection<Class<? extends IFloodlightService>> l 
+			= new ArrayList<Class<? extends IFloodlightService>>();
 		l.add(IFloodlightProviderService.class);
 		l.add(ITopologyService.class);
-		l.add(IBgpRouteService.class);
+		l.add(ITopoRouteService.class);
+		l.add(IDeviceService.class);
+		l.add(IRestApiService.class);
 		return l;
 	}
 	
@@ -75,31 +146,56 @@
 	public void init(FloodlightModuleContext context)
 			throws FloodlightModuleException {
 	    
+		initGateways();
+		
 	    ptree = new Ptree(32);
+	    
+	    routerIpAddresses = new HashSet<InetAddress>();
 		
 		// Register floodlight provider and REST handler.
 		floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
-		restApi = context.getServiceImpl(IRestApiService.class);
 		topology = context.getServiceImpl(ITopologyService.class);
+		topoRouteService = context.getServiceImpl(ITopoRouteService.class);
+		devices = context.getServiceImpl(IDeviceService.class);
+		restApi = context.getServiceImpl(IRestApiService.class);		
 		
+		//Read in config values
+		bgpdRestIp = context.getConfigParams(this).get("BgpdRestIp");
+		if (bgpdRestIp == null){
+			log.error("BgpdRestIp property not found in config file");
+			System.exit(1);
+		}
+		else {
+			log.info("BgpdRestIp set to {}", bgpdRestIp);
+		}
+		
+		routerId = context.getConfigParams(this).get("RouterId");
+		if (routerId == null){
+			log.error("RouterId property not found in config file");
+			System.exit(1);
+		}
+		else {
+			log.info("RouterId set to {}", routerId);
+		}
 		// Test.
 		//test();
-		  
 	}
 
 	public Ptree getPtree() {
 		return ptree;
 	}
-	public void  clearPtree() {
-		ptree = null;
-		ptree = new Ptree(32);
-		
+	
+	public void clearPtree() {
+		//ptree = null;
+		ptree = new Ptree(32);	
 	}
+	
 	public String getBGPdRestIp() {
-		return BGPdRestIp;
+		return bgpdRestIp;
 	}
+	
 	public String getRouterId() {
-		return RouterId;
+		return routerId;
 	}
 	
 	// Return nexthop address as byte array.
@@ -114,17 +210,20 @@
             log.debug("lookupRib: ptree node null");
 			return null;
 		}
+		
 		if (node.rib == null) {
             log.debug("lookupRib: ptree rib null");
 			return null;
 		}
+		
 		ptree.delReference(node);
 		
 		return node.rib;
 	}
 	
+	//TODO looks like this should be a unit test
 	@SuppressWarnings("unused")
-    private void test() {
+    private void test() throws UnknownHostException {
 		System.out.println("Here it is");
 		Prefix p = new Prefix("128.0.0.0", 8);
 		Prefix q = new Prefix("8.0.0.0", 8);
@@ -179,119 +278,308 @@
 
 	}
 	
+	private void retrieveRib(){
+		String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
+		String response = RestClient.get(url);
+		
+		if (response.equals("")){
+			return;
+		}
+		
+		response = response.replaceAll("\"", "'");
+		JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(response);  
+		JSONArray rib_json_array = jsonObj.getJSONArray("rib");
+		String router_id = jsonObj.getString("router-id");
+
+		int size = rib_json_array.size();
+
+		log.info("Retrived RIB of {} entries from BGPd", size);
+		
+		for (int j = 0; j < size; j++) {
+			JSONObject second_json_object = rib_json_array.getJSONObject(j);
+			String prefix = second_json_object.getString("prefix");
+			String nexthop = second_json_object.getString("nexthop");
+
+			//insert each rib entry into the local rib;
+			String[] substring = prefix.split("/");
+			String prefix1 = substring[0];
+			String mask1 = substring[1];
+
+			Prefix p;
+			try {
+				p = new Prefix(prefix1, Integer.valueOf(mask1));
+			} catch (NumberFormatException e) {
+				log.warn("Wrong mask format in RIB JSON: {}", mask1);
+				continue;
+			} catch (UnknownHostException e1) {
+				log.warn("Wrong prefix format in RIB JSON: {}", prefix1);
+				continue;
+			}
+			
+			PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
+			Rib rib = new Rib(router_id, nexthop, p.masklen);
+			
+			if (node.rib != null) {
+				node.rib = null;
+				ptree.delReference(node);
+			}
+			
+			node.rib = rib;
+			
+			prefixAdded(node);
+		} 
+	}
+	
+	public void prefixAdded(PtreeNode node) {
+		//Add a flow to rewrite mac for this prefix to all border switches
+		GatewayRouter thisRouter = null;
+		for (GatewayRouter router : gatewayRouters){	
+			if (router.getRouterIp().value() == 
+					InetAddresses.coerceToInteger(node.rib.nextHop)){
+				thisRouter = router;
+				break;
+			}
+		}
+		
+		if (thisRouter == null){
+			//TODO local router isn't in gateway list so this will get thrown
+			//Need to work out what to do about local prefixes with next hop 0.0.0.0.
+			log.error("Couldn't find next hop router in router {} in config"
+					, node.rib.nextHop.toString());
+			return; //just quit out here? This is probably a configuration error
+		}
+
+		for (GatewayRouter ingressRouter : gatewayRouters){
+			if (ingressRouter == thisRouter) {
+				continue;
+			}
+			
+			DataPath shortestPath = topoRouteService.getShortestPath(
+					ingressRouter.getAttachmentPoint(), 
+					thisRouter.getAttachmentPoint());
+			
+			if (shortestPath == null){
+				log.debug("Shortest path between {} and {} not found",
+						ingressRouter.getAttachmentPoint(), 
+						thisRouter.getAttachmentPoint());
+				return; // just quit here?
+			}
+			
+			//TODO check the shortest path against the cached version we
+			//calculated before. If they don't match up that's a problem
+			
+			//Set up the flow mod
+			OFFlowMod fm =
+	                (OFFlowMod) floodlightProvider.getOFMessageFactory()
+	                                              .getMessage(OFType.FLOW_MOD);
+			
+	        fm.setIdleTimeout((short)0)
+	        .setHardTimeout((short)0)
+	        .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+	        .setCookie(MAC_RW_COOKIE)
+	        .setCommand(OFFlowMod.OFPFC_ADD)
+	        //.setMatch(match)
+	        //.setActions(actions)
+	        .setPriority(SDNIP_PRIORITY)
+	        .setLengthU(OFFlowMod.MINIMUM_LENGTH
+	        		+ OFActionDataLayerDestination.MINIMUM_LENGTH
+	        		+ OFActionOutput.MINIMUM_LENGTH);
+	        
+	        OFMatch match = new OFMatch();
+	        match.setDataLayerType(Ethernet.TYPE_IPv4);
+	        match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+	        
+	        match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
+	        match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
+	        
+	        //match.setDataLayerDestination(ingressRouter.getSdnRouterMac().toBytes());
+	        //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+
+	        InetAddress address = null;
+	        try {
+				address = InetAddress.getByAddress(node.key);
+			} catch (UnknownHostException e1) {
+				//Should never happen is the reverse conversion has already been done
+				log.error("Malformed IP address");
+				return;
+			}
+	        
+	        match.setFromCIDR(address.getHostAddress() + "/" + node.rib.masklen, OFMatch.STR_NW_DST);
+	        fm.setMatch(match);
+	        
+	        //Set up MAC rewrite action
+	        OFActionDataLayerDestination macRewriteAction = new OFActionDataLayerDestination();
+	        macRewriteAction.setDataLayerAddress(thisRouter.getRouterMac().toBytes());
+	        
+	        //Set up output action
+	        OFActionOutput outputAction = new OFActionOutput();
+	        outputAction.setMaxLength((short)0xffff); //TODO check what this is (and if needed for mac rewrite)
+	        
+	        Port outputPort = shortestPath.flowEntries().get(0).outPort();
+	        outputAction.setPort(outputPort.value());
+	        
+	        List<OFAction> actions = new ArrayList<OFAction>();
+	        actions.add(macRewriteAction);
+	        actions.add(outputAction);
+	        fm.setActions(actions);
+	        
+	        //Write to switch
+	        IOFSwitch sw = floodlightProvider.getSwitches()
+	        		.get(ingressRouter.getAttachmentPoint().dpid().value());
+	        
+            if (sw == null){
+            	log.warn("Switch not found when pushing flow mod");
+            	continue;
+            }
+            
+            List<OFMessage> msglist = new ArrayList<OFMessage>();
+            msglist.add(fm);
+            try {
+				sw.write(msglist, null);
+				sw.flush();
+			} catch (IOException e) {
+				log.error("Failure writing flow mod", e);
+			}
+		}
+	}
+	
+	public void prefixDeleted(PtreeNode node) {
+		//Remove MAC rewriting flows from other border switches
+		
+	}
+	
+	/*
+	 * On startup we need to calculate a full mesh of paths between all gateway
+	 * switches
+	 */
+	private void calculateFullMesh(){
+		Map<IOFSwitch, SwitchPort> gatewaySwitches = new HashMap<IOFSwitch, SwitchPort>();
+		
+		//have to account for switches not being there, paths not being found.
+		
+		//for (SwitchPort switchPort : gatewayRouters){
+		for (GatewayRouter router : gatewayRouters){
+			SwitchPort switchPort = router.getAttachmentPoint();
+			
+			IOFSwitch sw = floodlightProvider.getSwitches().get(switchPort.dpid().value());
+			
+			if (sw == null){
+				log.debug("Gateway switch {} not here yet", switchPort.dpid().value());
+				return; // just quit here?
+			}
+			
+			//Only need to know 1 external-facing port from each gateway switch
+			//which we can feed into shortest path calculation
+			if (!gatewaySwitches.containsKey(sw)){
+				gatewaySwitches.put(sw, switchPort);
+			}
+			
+		}
+		log.debug("size {}", gatewaySwitches.size());
+		
+		//For each border router, calculate and install a path from every other
+		//border switch to said border router. However, don't install the entry
+		//in to the first hop switch, as we need to install an entry to rewrite
+		//for each prefix received. This will be done later when prefixes have 
+		//actually been received.
+		
+		for (GatewayRouter dstRouter : gatewayRouters){
+			SwitchPort routerAttachmentPoint = dstRouter.getAttachmentPoint();
+			for (Map.Entry<IOFSwitch, SwitchPort> src : gatewaySwitches.entrySet()) {
+		
+				if (routerAttachmentPoint.dpid().value() == 
+						src.getKey().getId()){
+					continue;
+				}
+				
+				DataPath shortestPath = topoRouteService.getShortestPath(
+						src.getValue(), routerAttachmentPoint);
+				
+				if (shortestPath == null){
+					log.debug("Shortest path between {} and {} not found",
+							src.getValue(), routerAttachmentPoint);
+					return; // just quit here?
+				}
+				
+				//install flows
+				installPath(shortestPath.flowEntries(), dstRouter);
+			}
+		}
+	}
+	
+	private void installPath(List<FlowEntry> flowEntries, GatewayRouter router){
+
+		//Set up the flow mod
+		OFFlowMod fm =
+                (OFFlowMod) floodlightProvider.getOFMessageFactory()
+                                              .getMessage(OFType.FLOW_MOD);
+		
+        OFActionOutput action = new OFActionOutput();
+        action.setMaxLength((short)0xffff);
+        List<OFAction> actions = new ArrayList<OFAction>();
+        actions.add(action);
+        
+        fm.setIdleTimeout((short)0)
+        .setHardTimeout((short)0)
+        .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+        .setCookie(L2_FWD_COOKIE)
+        .setCommand(OFFlowMod.OFPFC_ADD)
+        //.setMatch(match)
+        .setActions(actions)
+        .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+        
+        //Don't push the first flow entry. We need to push entries in the
+		//first switch based on IP prefix which we don't know yet.
+        for (int i = 1; i < flowEntries.size(); i++){        	
+        	FlowEntry flowEntry = flowEntries.get(i);
+           
+            OFMatch match = new OFMatch();
+            match.setDataLayerDestination(router.getRouterMac().toBytes());
+            match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+            ((OFActionOutput) fm.getActions().get(0)).setPort(flowEntry.outPort().value());
+            
+            fm.setMatch(match);
+            
+            IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
+            
+            if (sw == null){
+            	log.warn("Switch not found when pushing flow mod");
+            	continue;
+            }
+            
+            List<OFMessage> msglist = new ArrayList<OFMessage>();
+            msglist.add(fm);
+            try {
+				sw.write(msglist, null);
+				sw.flush();
+			} catch (IOException e) {
+				log.error("Failure writing flow mod", e);
+			}
+            
+            try {
+                fm = fm.clone();
+            } catch (CloneNotSupportedException e1) {
+                log.error("Failure cloning flow mod", e1);
+            }
+		}
+	}
+	
 	@Override
 	public void startUp(FloodlightModuleContext context) {
 		restApi.addRestletRoutable(new BgpRouteWebRoutable());
-		topology.addListener((ITopologyListener) this);
+		topology.addListener(this);
 		
-		 // get the BGPdRestIp and RouterId from transit-route-pusher.py
-  		File file = new File("/home/ubuntu/sdn/transit-route-pusher.py");  
-       
-        
-    try{  
-        BufferedReader input = new BufferedReader (new FileReader(file));  
-        String text; 
-        int is_BGPdRestIp=0;
-        int is_RouterId=0;
-        								                         
-        while((text = input.readLine()) != null && (is_BGPdRestIp == 0) || (is_RouterId == 0) ){  
-        					  
-        		if(is_BGPdRestIp == 1 && is_RouterId ==1)
-        		{break;}
-        		
-        				if(is_BGPdRestIp == 0 && text.contains("BGPdRestIp") ){
-		        				String[] temp =	text.split("\"");
-		        				BGPdRestIp = temp[1];
-		        				is_BGPdRestIp = 1;
-		        				
-        				
-            	}else if (is_RouterId == 0 && text.contains("RouterId") ){
-												
-		            	String[] temp =	text.split("\"");
-		    							RouterId = temp[1];
-		    							is_RouterId = 1;
-		    							
-    							
-									}
-        						
-        					}
-    
-                   
-    }  catch(Exception e){  
-       e.printStackTrace();
-           }  
-      
-		        
-			// automatically get the rib from bgpd at the ONOS initiation process.
-    String dest=RouterId;
-    String str="http://"+BGPdRestIp+"/wm/bgp/"+dest;
-	 		
-	          
-	try {
-	       	 
-				URL url = new URL(str);
-				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
-				conn.setRequestMethod("GET");
-				conn.setRequestProperty("Accept", "application/json");
-		 
-				if (conn.getResponseCode() != 200) {
-					throw new RuntimeException("Failed : HTTP error code : "
-							+ conn.getResponseCode());
-				}
-	
-			 BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream()))); 
-			 StringBuffer res = new StringBuffer();
-			 String line;
-			 while ((line = br.readLine()) != null) {
-				 	res.append(line);
-			 	}	   
-			 
-			 String res2=res.toString().replaceAll("\"", "'");
-			 JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(res2);  
-			 JSONArray rib_json_array = jsonObj.getJSONArray("rib");
-			 String router_id = jsonObj.getString("router-id");
-			       
-			 int size = rib_json_array.size();
-			 System.out.print("size:"+size+"\n");
-			 for (int j = 0; j < size; j++) {
-	        JSONObject second_json_object = rib_json_array.getJSONObject(j);
-	        String prefix = second_json_object.getString("prefix");
-	        String nexthop = second_json_object.getString("nexthop");
-	        
-	        //insert each rib entry into the local rib;
-	        String[] substring= prefix.split("/");
-	        String prefix1=substring[0];
-	        String mask1=substring[1];
-	        			
-						Prefix p = new Prefix(prefix1, Integer.valueOf(mask1));
-						PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
-						Rib rib = new Rib(router_id, nexthop, p.masklen);
-			
-						if (node.rib != null) {
-							node.rib = null;
-							ptree.delReference(node);
-						}
-						node.rib = rib;
-      
-			 }  
-			 br.close();
-			 conn.disconnect();
-
-			} catch (MalformedURLException e) {
-
-				e.printStackTrace();
-
-			} catch (IOException e) {
-
-				e.printStackTrace();
-
-			}
-	
-
+		//Retrieve the RIB from BGPd during startup
+		retrieveRib();
+		
+		//Don't have to do this as we'll never have switches connected here
+		//calculateFullMesh();
 	}
 
 	@Override
 	public void topologyChanged() {
+		//Probably need to look at all changes, not just port changes
+		/*
 		boolean change = false;
 		String changelog = "";
 		
@@ -307,7 +595,15 @@
 		log.info ("received topo change" + changelog);
 
 		if (change) {
-			RestClient.get ("http://localhost:5000/topo_change");
+			//RestClient.get ("http://localhost:5000/topo_change");
 		}
+		*/
+		
+		for (LDUpdate update : topology.getLastLinkUpdates()){
+			log.debug("{} event causing internal L2 path recalculation",
+					update.getOperation().toString());
+			
+		}
+		calculateFullMesh();
 	}
 }
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
index 28d9621..4ad1e95 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
@@ -1,193 +1,212 @@
 package net.floodlightcontroller.bgproute;
 
+import java.net.UnknownHostException;
+
+import org.restlet.resource.Delete;
 import org.restlet.resource.Get;
 import org.restlet.resource.Post;
-import org.restlet.resource.Delete;
 import org.restlet.resource.ServerResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import net.floodlightcontroller.restclient.RestClient;
-import java.io.UnsupportedEncodingException;  
-import java.nio.ByteBuffer;  
 
 public class BgpRouteResource extends ServerResource {
-    
-	protected static Logger log = LoggerFactory
-            .getLogger(BgpRouteResource.class);
-	
+
+	protected static Logger log = LoggerFactory.getLogger(BgpRouteResource.class);
+
 	private String addrToString(byte [] addr) {
-	    String str = "";
-		
+		String str = "";
+
 		for (int i = 0; i < 4; i++) {
 			int val = (addr[i] & 0xff);
 			str += val;
 			if (i != 3)
 				str += ".";
 		}
-		
+
 		return str;
 	}
-	
-	@SuppressWarnings("unused")
+
+	//@SuppressWarnings("unused")
 	@Get
-		public String get(String fmJson) {		
-		String linpp=fmJson;
-			String dest = (String) getRequestAttributes().get("dest");
-			String output = "";
-			IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
-	                get(IBgpRouteService.class.getCanonicalName());
+	public String get(String fmJson) {
+		String dest = (String) getRequestAttributes().get("dest");
+		String output = "";
+		IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
+				get(IBgpRouteService.class.getCanonicalName());
+
+		if (dest != null) {
+			//TODO Needs to be changed to use the new RestClient.get().
 			
-			if (dest != null) {
-				Prefix p = new Prefix(dest, 32);
-				if (p == null) {
-					return "[GET]: dest address format is wrong";
-				}
-							
-				// the dest here refers to router-id
-				//BGPdRestIp includes port number, such as 1.1.1.1:8080
-				String BGPdRestIp = bgpRoute.getBGPdRestIp();
-				String url="http://"+BGPdRestIp+"/wm/bgp/"+dest;
-				
-							
-				
-				RestClient.get(url);
-				output="Get rib from bgpd finished!\n";
-				return output;
 			
-			} else {
-				Ptree ptree = bgpRoute.getPtree();
-				output += "{\n  \"rib\": [\n";
-				boolean printed = false;
-				for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
-					if (node.rib == null) {
-						continue;
-					}
-					if (printed == true) {
-						output += ",\n";
-					}
-					output += "    {\"prefix\": \"" + addrToString(node.key) + "/" + node.keyBits +"\", ";
-					output += "\"nexthop\": \"" + addrToString(node.rib.nextHop.getAddress()) +"\"}";
-					printed = true;
-				}
-				//output += "{\"router_id\": \"" + addrToString(node.rib.routerId.getAddress()) +"\"}\n";
-				output += "\n  ]\n}\n";
-	   
-			}
+			//Prefix p;
+			//try {
+			//	p = new Prefix(dest, 32);
+			//} catch (UnknownHostException e) {
+			//if (p == null) {
+			//	return "[GET]: dest address format is wrong";
+			//}
+
+			// the dest here refers to router-id
+			//bgpdRestIp includes port number, such as 1.1.1.1:8080
+			String BGPdRestIp = bgpRoute.getBGPdRestIp();
+			String url="http://"+BGPdRestIp+"/wm/bgp/"+dest;
+
+			RestClient.get(url);
+			
+			output="Get rib from bgpd finished!\n";
 			return output;
+		} 
+		else {
+			Ptree ptree = bgpRoute.getPtree();
+			output += "{\n  \"rib\": [\n";
+			boolean printed = false;
+			
+			for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
+				if (node.rib == null) {
+					continue;
+				}
+				if (printed == true) {
+					output += ",\n";
+				}
+				output += "    {\"prefix\": \"" + addrToString(node.key) + "/" + node.keyBits +"\", ";
+				output += "\"nexthop\": \"" + addrToString(node.rib.nextHop.getAddress()) +"\"}";
+				printed = true;
+			}
+			//output += "{\"router_id\": \"" + addrToString(node.rib.routerId.getAddress()) +"\"}\n";
+			output += "\n  ]\n}\n";
 		}
+		
+		return output;
+	}
 
-	public static ByteBuffer toByteBuffer(String value) throws UnsupportedEncodingException
-	  {
-	  return ByteBuffer.wrap(value.getBytes("UTF-8"));
-	  }
+	//unused?
+	/*
+	public static ByteBuffer toByteBuffer(String value) throws UnsupportedEncodingException {
+		return ByteBuffer.wrap(value.getBytes("UTF-8"));
+	}
+	*/
 
-public static String toString(ByteBuffer buffer) throws UnsupportedEncodingException
-	 {
-	    byte[] bytes = new byte[buffer.remaining()];
-	    buffer.get(bytes);
-	    return new String(bytes, "UTF-8");
-	    
-	   }
+	//unused?
+	/*
+	public static String toString(ByteBuffer buffer) throws UnsupportedEncodingException {
+		byte[] bytes = new byte[buffer.remaining()];
+		buffer.get(bytes);
+		return new String(bytes, "UTF-8");
+	}
+	*/
 
-	
 	@Post
 	public String store(String fmJson) {
-        IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
-                get(IBgpRouteService.class.getCanonicalName());
- 	
-	  Ptree ptree = bgpRoute.getPtree();
-	
-		String router_id = (String) getRequestAttributes().get("routerid");
+		IBgpRouteService bgpRoute = (IBgpRouteService) getContext().getAttributes().
+				get(IBgpRouteService.class.getCanonicalName());
+
+		Ptree ptree = bgpRoute.getPtree();
+
+		String routerId = (String) getRequestAttributes().get("routerid");
 		String prefix = (String) getRequestAttributes().get("prefix");
 		String mask = (String) getRequestAttributes().get("mask");
 		String nexthop = (String) getRequestAttributes().get("nexthop");
 		String capability = (String) getRequestAttributes().get("capability");
-		
-	
+
 		String reply = "";
-		
+
 		if (capability == null) {
-		
 			// this is a prefix add
-			Prefix p = new Prefix(prefix, Integer.valueOf(mask));
+			Prefix p;
+			try {
+				p = new Prefix(prefix, Integer.valueOf(mask));
+			} catch (NumberFormatException e) {
+				reply = "[POST: mask format is wrong]";
+				log.info(reply);
+				return reply + "\n";				
+			} catch (UnknownHostException e1) {
+				reply = "[POST: prefix format is wrong]";
+				log.info(reply);
+				return reply + "\n";
+			}
+			
 			PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
-			Rib rib = new Rib(router_id, nexthop, p.masklen);
+			Rib rib = new Rib(routerId, nexthop, p.masklen);
 
 			if (node.rib != null) {
 				node.rib = null;
 				ptree.delReference(node);
 			}
 			node.rib = rib;
+
+			bgpRoute.prefixAdded(node);
 			
 			reply = "[POST: " + prefix + "/" + mask + ":" + nexthop + "]";
 			log.info(reply);
-	
-			
-		}else if(capability.equals("1")){
-			reply = "[POST-capability: " + capability + "]\n";
-			log.info(reply);
-			// to store the number in the top node of the Ptree	
-			
-		}else{			
-			reply = "[POST-capability: " + capability + "]\n";
-			log.info(reply);
-			// to store the number in the top node of the Ptree	
-	
 		}
-		
-		
+		else if(capability.equals("1")) {
+			reply = "[POST-capability: " + capability + "]\n";
+			log.info(reply);
+			// to store the number in the top node of the Ptree	
+		}
+		else {			
+			reply = "[POST-capability: " + capability + "]\n";
+			log.info(reply);
+			// to store the number in the top node of the Ptree	
+		}
+
 		return reply + "\n";
-		
-	
 	}
-	
+
 	@Delete
 	public String delete(String fmJson) {
 		IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
-                get(IBgpRouteService.class.getCanonicalName());
+				get(IBgpRouteService.class.getCanonicalName());
 
-   Ptree ptree = bgpRoute.getPtree();
-      	
+		Ptree ptree = bgpRoute.getPtree();
+
 		String routerId = (String) getRequestAttributes().get("routerid");
 		String prefix = (String) getRequestAttributes().get("prefix");
 		String mask = (String) getRequestAttributes().get("mask");
 		String nextHop = (String) getRequestAttributes().get("nexthop");
 		String capability = (String) getRequestAttributes().get("capability");
-		
+
 		String reply = "";
-		
+
 		if (capability == null) {
-					// this is a prefix delete
-					Prefix p = new Prefix(prefix, Integer.valueOf(mask));
-										
-					PtreeNode node = ptree.lookup(p.getAddress(), p.masklen);
-									
-					Rib r = new Rib(routerId, nextHop, p.masklen);
-					
-					if (node != null && node.rib != null) {
-						
-						if (r.equals(node.rib)) {
-							
-							node.rib = null;
-							ptree.delReference(node);					
-						}
-					}
-					
-							
-					reply =reply + "[DELE: " + prefix + "/" + mask + ":" + nextHop + "]";
-					
-		}else {
+			// this is a prefix delete
+			Prefix p;
+			try {
+				p = new Prefix(prefix, Integer.valueOf(mask));
+			} catch (NumberFormatException e) {
+				reply = "[DELE: mask format is wrong]";
+				log.info(reply);
+				return reply + "\n";
+			} catch (UnknownHostException e1) {
+				reply = "[DELE: prefix format is wrong]";
+				log.info(reply);
+				return reply + "\n";
+			}
+
+			PtreeNode node = ptree.lookup(p.getAddress(), p.masklen);
+
+			Rib r = new Rib(routerId, nextHop, p.masklen);
+
+			if (node != null && node.rib != null) {
+				if (r.equals(node.rib)) {
+					node.rib = null;
+					ptree.delReference(node);					
+				}
+			}
+
+			bgpRoute.prefixDeleted(node);
 			
+			reply =reply + "[DELE: " + prefix + "/" + mask + ":" + nextHop + "]";
+		}
+		else {
 			// clear the local rib: Ptree			
 			bgpRoute.clearPtree();
 			reply = "[DELE-capability: " + capability + "; The local Rib is cleared!]\n";
-			
-			
+
 			// to store the number in the top node of the Ptree	
-				
-		}	
+		}
+		
 		log.info(reply);
-	
 		return reply + "\n";
 	}
 }
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResourceSynch.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResourceSynch.java
index d0c337a..ff7aea6 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResourceSynch.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResourceSynch.java
@@ -6,7 +6,6 @@
 import org.restlet.resource.ServerResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import net.floodlightcontroller.restclient.RestClient;
 
 
 public class BgpRouteResourceSynch extends ServerResource {
@@ -29,7 +28,7 @@
 				
 			String BGPdRestIp = bgpRoute.getBGPdRestIp();	
 				
-			//BGPdRestIp includes port number, such as 1.1.1.1:8080
+			//bgpdRestIp includes port number, such as 1.1.1.1:8080
 			RestClient.post("http://"+BGPdRestIp+"/wm/bgp/"+router_id+"/"+prefix+"/"+mask+"/"+nexthop);
 			}catch(Exception e)
 			{e.printStackTrace();}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/GatewayRouter.java b/src/main/java/net/floodlightcontroller/bgproute/GatewayRouter.java
new file mode 100644
index 0000000..56d5243
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/GatewayRouter.java
@@ -0,0 +1,46 @@
+package net.floodlightcontroller.bgproute;
+
+import net.floodlightcontroller.util.IPv4;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.SwitchPort;
+
+public class GatewayRouter {
+	private SwitchPort attachmentPoint;
+	private MACAddress routerMac;
+	private IPv4 routerIp;
+	
+	//For now, put in the IP and MAC of the SDN domain's router that this 
+	//gateway will be communicating with
+	private MACAddress sdnRouterMac;
+	private IPv4 sdnRouterIp;
+	
+	public GatewayRouter(SwitchPort attachmentPoint, MACAddress routerMac, 
+			IPv4 routerIp, MACAddress sdnRouterMac, IPv4 sdnRouterIp) {
+		this.attachmentPoint = attachmentPoint;
+		this.routerMac = routerMac;
+		this.routerIp = routerIp;
+		this.sdnRouterIp = sdnRouterIp;
+		this.sdnRouterMac = sdnRouterMac;
+	}
+
+	public SwitchPort getAttachmentPoint() {
+		return attachmentPoint;
+	}
+
+	public MACAddress getRouterMac() {
+		return routerMac;
+	}
+
+	public IPv4 getRouterIp() {
+		return routerIp;
+	}
+	
+	//TODO delete if not needed
+	public MACAddress getSdnRouterMac() {
+		return sdnRouterMac;
+	}
+	
+	public IPv4 getSdnRouterIp() {
+		return sdnRouterIp;
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java b/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
index a6025ef..0e95d9e 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
@@ -4,15 +4,17 @@
 
 public interface IBgpRouteService extends IFloodlightService {
 
-    public Rib lookupRib(byte[] dest);
-    
-    public Ptree getPtree();
-    
-    public String getBGPdRestIp();
-    
-    public String getRouterId();
-    
-	  public void  clearPtree() ;
-       
-    
+	public Rib lookupRib(byte[] dest);
+
+	public Ptree getPtree();
+
+	public String getBGPdRestIp();
+
+	public String getRouterId();
+
+	public void clearPtree();
+	
+	//TODO This functionality should be provided by some sort of Ptree listener framework
+	public void prefixAdded(PtreeNode node);
+	public void prefixDeleted(PtreeNode node);
 }
diff --git a/src/main/java/net/floodlightcontroller/bgproute/Prefix.java b/src/main/java/net/floodlightcontroller/bgproute/Prefix.java
index 7ba014b..980646f 100644
--- a/src/main/java/net/floodlightcontroller/bgproute/Prefix.java
+++ b/src/main/java/net/floodlightcontroller/bgproute/Prefix.java
@@ -7,28 +7,27 @@
 	public int masklen;
 	protected InetAddress address;
 
-	Prefix(byte[] addr, int masklen) {
-		try {
-			address = InetAddress.getByAddress(addr);
-		} catch (UnknownHostException e) {
-			System.out.println("InetAddress exception");
-			return;
-		}
+	public Prefix(byte[] addr, int masklen) throws UnknownHostException {
+		//try {
+		address = InetAddress.getByAddress(addr);
+		//} catch (UnknownHostException e) {
+		//	System.out.println("InetAddress exception");
+		//	return;
+		//}
 		this.masklen = masklen;
-		System.out.println(address.toString() + "/" + masklen);
+		//System.out.println(address.toString() + "/" + masklen);
 	}
-	
-	Prefix(String str, int masklen) {
-		try {
-			address = InetAddress.getByName(str);
-			//System.out.println(address.toString());
-		} catch (UnknownHostException e) {
-			System.out.println("InetAddress exception");
-			return;
-		}
+
+	public Prefix(String str, int masklen) throws UnknownHostException {
+		//try {
+		address = InetAddress.getByName(str);
+		//} catch (UnknownHostException e) {
+		//	System.out.println("InetAddress exception");
+		//	return;
+		//}
 		this.masklen = masklen;
 	}
-	
+
 	public byte [] getAddress() {
 		return address.getAddress();
 	}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/RestClient.java b/src/main/java/net/floodlightcontroller/bgproute/RestClient.java
new file mode 100644
index 0000000..ef5e38b
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/RestClient.java
@@ -0,0 +1,104 @@
+package net.floodlightcontroller.bgproute;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class RestClient {
+	protected static Logger log = LoggerFactory.getLogger(RestClient.class);
+
+	public static String get(String str) {
+		StringBuilder response = new StringBuilder();
+
+		try {
+
+			URL url = new URL(str);
+			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+			conn.setConnectTimeout(2 * 1000); //2 seconds
+			conn.setRequestMethod("GET");
+			conn.setRequestProperty("Accept", "application/json");
+
+			if (conn.getResponseCode() != 200) {
+				throw new RuntimeException("Failed : HTTP error code : "
+						+ conn.getResponseCode());
+			}
+
+			if (!conn.getContentType().equals("application/json")){	
+				log.warn("The content received from {} is not json", str);
+			}		
+
+			BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream()))); 
+			String line;
+			while ((line = br.readLine()) != null) {
+				response.append(line);
+			}
+			
+			br.close();
+			conn.disconnect();
+			
+		} catch (MalformedURLException e) {
+			log.error("Malformed URL for GET request", e);
+		} catch (ConnectTimeoutException e) {
+			log.warn("Couldn't connect remote REST server");
+		} catch (IOException e) {
+			log.warn("Couldn't connect remote REST server");
+		}
+		
+		return response.toString();
+	}
+
+	public static void post (String str) {
+
+		try {
+			URL url = new URL(str);
+			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+			conn.setDoOutput(true);
+			conn.setRequestMethod("POST");
+			conn.setRequestProperty("Content-Type", "application/json");		
+
+			if (conn.getResponseCode() != 200) {
+				throw new RuntimeException("Failed : HTTP error code : "
+						+ conn.getResponseCode());
+			}
+
+			conn.disconnect();
+
+		} catch (MalformedURLException e) {
+			log.error("Malformed URL for GET request", e);
+		} catch (IOException e) {
+			log.warn("Couldn't connect remote REST server");
+		}
+	}
+
+
+	public static void delete (String str) {
+
+		try {
+			URL url = new URL(str);
+			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+			conn.setRequestMethod("DELETE");
+			conn.setRequestProperty("Accept", "application/json");
+
+
+			if (conn.getResponseCode() != 200) {
+				throw new RuntimeException("Failed : HTTP error code : "
+						+ conn.getResponseCode());
+			}
+
+			conn.disconnect();
+
+		} catch (MalformedURLException e) {
+			log.error("Malformed URL for GET request", e);
+		} catch (IOException e) {
+			log.warn("Couldn't connect remote REST server");
+		}
+	}
+}
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 307919a..8aec20f 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -1,6 +1,8 @@
 package net.floodlightcontroller.flowcache;
 
 import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -11,6 +13,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -87,9 +90,16 @@
     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 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);
@@ -269,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++;
@@ -344,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
@@ -369,7 +381,7 @@
 		    conn.utils().removeFlowPath(conn, flowPathObj);
 		}
 
-		topoRouteService.dropShortestPathTopo();
+		topoRouteService.dropShortestPathTopo(shortestPathTopo);
 
 		conn.endTx(Transaction.COMMIT);
 
@@ -462,7 +474,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
@@ -531,8 +543,14 @@
 	} catch (Exception e) {
 	    // TODO: handle exceptions
 	    conn.endTx(Transaction.ROLLBACK);
-	    log.error(":addFlow FlowId:{} failed",
-		      flowPath.flowId().toString());
+
+	    StringWriter sw = new StringWriter();
+	    e.printStackTrace(new PrintWriter(sw));
+	    String stacktrace = sw.toString();
+
+	    log.error(":addFlow FlowId:{} failed: {}",
+		      flowPath.flowId().toString(),
+		      stacktrace);
 	}
 	if (flowObj == null) {
 	    log.error(":addFlow FlowId:{} failed: Flow object not created",
@@ -745,6 +763,73 @@
     }
 
     /**
+     * Delete all previously added flows.
+     *
+     * @return true on success, otherwise false.
+     */
+    @Override
+    public boolean deleteAllFlows() {
+	List<Thread> threads = new LinkedList<Thread>();
+	final ConcurrentLinkedQueue<FlowId> concurrentAllFlowIds =
+	    new ConcurrentLinkedQueue<FlowId>();
+
+	// Get all Flow IDs
+	Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
+	for (IFlowPath flowPathObj : allFlowPaths) {
+	    if (flowPathObj == null)
+		continue;
+	    String flowIdStr = flowPathObj.getFlowId();
+	    if (flowIdStr == null)
+		continue;
+	    FlowId flowId = new FlowId(flowIdStr);
+	    concurrentAllFlowIds.add(flowId);
+	}
+
+	// Delete all flows one-by-one
+	for (FlowId flowId : concurrentAllFlowIds)
+	    deleteFlow(flowId);
+
+	/*
+	 * TODO: A faster mechanism to delete the Flow Paths by using
+	 * a number of threads. Commented-out for now.
+	 */
+	/*
+	//
+	// Create the threads to delete the Flow Paths
+	//
+	for (int i = 0; i < 10; i++) {
+	    Thread thread = new Thread(new Runnable() {
+		@Override
+		public void run() {
+		    while (true) {
+			FlowId flowId = concurrentAllFlowIds.poll();
+			if (flowId == null)
+			    return;
+			deleteFlow(flowId);
+		    }
+		}}, "Delete All Flow Paths");
+	    threads.add(thread);
+	}
+
+	// Start processing
+	for (Thread thread : threads) {
+	    thread.start();
+	}
+
+	// Wait for all threads to complete
+	for (Thread thread : threads) {
+	    try {
+		thread.join();
+	    } catch (InterruptedException e) {
+		log.debug("Exception waiting for a thread to delete a Flow Path: ", e);
+	    }
+	}
+	*/
+
+	return true;
+    }
+
+    /**
      * Delete a previously added flow.
      *
      * @param flowId the Flow ID of the flow to delete.
@@ -807,6 +892,35 @@
     }
 
     /**
+     * Clear the state for all previously added flows.
+     *
+     * @return true on success, otherwise false.
+     */
+    @Override
+    public boolean clearAllFlows() {
+	List<FlowId> allFlowIds = new LinkedList<FlowId>();
+
+	// Get all Flow IDs
+	Iterable<IFlowPath> allFlowPaths = conn.utils().getAllFlowPaths(conn);
+	for (IFlowPath flowPathObj : allFlowPaths) {
+	    if (flowPathObj == null)
+		continue;
+	    String flowIdStr = flowPathObj.getFlowId();
+	    if (flowIdStr == null)
+		continue;
+	    FlowId flowId = new FlowId(flowIdStr);
+	    allFlowIds.add(flowId);
+	}
+
+	// Clear all flows one-by-one
+	for (FlowId flowId : allFlowIds) {
+	    clearFlow(flowId);
+	}
+
+	return true;
+    }
+
+    /**
      * Clear the state for a previously added flow.
      *
      * @param flowId the Flow ID of the flow to clear.
@@ -1763,4 +1877,209 @@
 	//
 	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 synchronized FlowPath measurementStorePathFlow(FlowPath flowPath) {
+	//
+	// Prepare the Shortest Path computation if the first Flow Path
+	//
+	if (measurementStoredPaths.isEmpty())
+	    measurementShortestPathTopo = topoRouteService.prepareShortestPathTopo();
+
+	//
+	// Compute the Shortest Path
+	//
+	DataPath dataPath =
+	    topoRouteService.getTopoShortestPath(measurementShortestPathTopo,
+						 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 to the internal storage
+	//
+	measurementStoredPaths.add(computedFlowPath);
+
+	log.debug("Measurement storing path {}",
+		  computedFlowPath.flowId().toString());
+
+	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) {
+	// 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());
+
+	//
+	// Create the threads to install the Flow Paths
+	//
+	for (int i = 0; i < numThreads; i++) {
+	    MyThread thread = new MyThread();
+	    thread.threadId = i;
+	    threads.add(thread);
+	}
+
+	//
+	// Start processing
+	//
+	measurementEndTimeProcessingPaths = 0;
+	measurementStartTimeProcessingPaths = System.nanoTime();
+	for (Thread thread : threads) {
+	    thread.start();
+	}
+
+	// Wait for all threads to complete
+	for (Thread thread : threads) {
+	    try {
+		thread.join();
+	    } catch (InterruptedException e) {
+		log.debug("Exception waiting for a thread to install a Flow Path: ", e);
+	    }
+	}
+
+	// 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;
+    }
+
+    /**
+     * 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 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.
+     */
+    @Override
+    public boolean measurementClearAllPaths() {
+	measurementStoredPaths.clear();
+	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 855f064..6c19fd0 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -29,6 +29,13 @@
 		    String dataPathSummaryStr);
 
     /**
+     * Delete all previously added flows.
+     *
+     * @return true on success, otherwise false.
+     */
+    boolean deleteAllFlows();
+
+    /**
      * Delete a previously added flow.
      *
      * @param flowId the Flow ID of the flow to delete.
@@ -37,6 +44,13 @@
     boolean deleteFlow(FlowId flowId);
 
     /**
+     * Clear the state for all previously added flows.
+     *
+     * @return true on success, otherwise false.
+     */
+    boolean clearAllFlows();
+
+    /**
      * Clear the state for a previously added flow.
      *
      * @param flowId the Flow ID of the flow to clear.
@@ -101,4 +115,47 @@
      * @return the added shortest-path flow on success, otherwise null.
      */
     public FlowPath addAndMaintainShortestPathFlow(FlowPath flowPath);
+
+    /**
+     * Store a path flow for measurement purpose.
+     *
+     * NOTE: The Flow Path argument does NOT contain flow entries.
+     *
+     * @param flowPath the Flow Path with the endpoints and the match
+     * conditions to store.
+     * @return the stored shortest-path flow on success, otherwise null.
+     */
+    public FlowPath measurementStorePathFlow(FlowPath flowPath);
+
+    /**
+     * 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.
+     */
+    public boolean measurementInstallPaths(Integer numThreads);
+
+    /**
+     * Get the measurement time that took to install the path flows.
+     *
+     * @return the measurement time (in nanoseconds) it took to install
+     * the path flows.
+     */
+    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.
+     */
+    public boolean measurementClearAllPaths();
 }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/ClearFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/ClearFlowResource.java
index 8fff358..7f3b589 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/ClearFlowResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/ClearFlowResource.java
@@ -27,11 +27,16 @@
 
 	// Extract the arguments
 	String flowIdStr = (String) getRequestAttributes().get("flow-id");
-	FlowId flowId = new FlowId(flowIdStr);
-	log.debug("Clear Flow Id: " + flowIdStr);
 
 	// Process the request
-	result = flowService.clearFlow(flowId);
+	if (flowIdStr.equals("all")) {
+	    log.debug("Clear All Flows");
+	    result = flowService.clearAllFlows();
+	} else {
+	    FlowId flowId = new FlowId(flowIdStr);
+	    log.debug("Clear Flow Id: " + flowIdStr);
+	    result = flowService.clearFlow(flowId);
+	}
 	return result;
     }
 }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/DeleteFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/DeleteFlowResource.java
index f418c1e..ed6f0f7 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/DeleteFlowResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/DeleteFlowResource.java
@@ -27,11 +27,16 @@
 
 	// Extract the arguments
 	String flowIdStr = (String) getRequestAttributes().get("flow-id");
-	FlowId flowId = new FlowId(flowIdStr);
-	log.debug("Delete Flow Id: " + flowIdStr);
 
 	// Process the request
-	result = flowService.deleteFlow(flowId);
+	if (flowIdStr.equals("all")) {
+	    log.debug("Delete All Flows");
+	    result = flowService.deleteAllFlows();
+	} else {
+	    FlowId flowId = new FlowId(flowIdStr);
+	    log.debug("Delete Flow Id: " + flowIdStr);
+	    result = flowService.deleteFlow(flowId);
+	}
 	return result;
     }
 }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
index 962dbbb..fd9a319 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
@@ -22,6 +22,11 @@
         router.attach("/getall-by-endpoints/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByEndpointsResource.class);
         router.attach("/getall/json", GetAllFlowsResource.class);
         router.attach("/getsummary/{flow-id}/{max-flows}/json", GetSummaryFlowsResource.class);
+        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/MeasurementClearAllPathsFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementClearAllPathsFlowResource.java
new file mode 100644
index 0000000..f69180d
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementClearAllPathsFlowResource.java
@@ -0,0 +1,35 @@
+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 MeasurementClearAllPathsFlowResource extends ServerResource {
+    protected static Logger log = LoggerFactory.getLogger(MeasurementClearAllPathsFlowResource.class);
+
+    @Get("json")
+    public Boolean retrieve() {
+	Boolean result = false;
+
+        IFlowService flowService =
+                (IFlowService)getContext().getAttributes().
+                get(IFlowService.class.getCanonicalName());
+
+        if (flowService == null) {
+	    log.debug("ONOS Flow Service not found");
+            return result;
+	}
+
+	// Extract the arguments
+	log.debug("Measurement Clear All Paths");
+
+	// Process the request
+	result = flowService.measurementClearAllPaths();
+	return result;
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetInstallPathsTimeNsecFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetInstallPathsTimeNsecFlowResource.java
new file mode 100644
index 0000000..3dc1d08
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementGetInstallPathsTimeNsecFlowResource.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 MeasurementGetInstallPathsTimeNsecFlowResource extends ServerResource {
+    protected static Logger log = LoggerFactory.getLogger(MeasurementGetInstallPathsTimeNsecFlowResource.class);
+
+    @Get("json")
+    public Long retrieve() {
+	Long 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.measurementGetInstallPathsTimeNsec();
+
+	log.debug("Measurement Get Install Paths Time (nsec): " + result);
+
+	return result;
+    }
+}
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/flowcache/web/MeasurementInstallPathsFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementInstallPathsFlowResource.java
new file mode 100644
index 0000000..1bba4b1
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementInstallPathsFlowResource.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 MeasurementInstallPathsFlowResource extends ServerResource {
+    protected static Logger log = LoggerFactory.getLogger(MeasurementInstallPathsFlowResource.class);
+
+    @Get("json")
+    public Boolean retrieve() {
+	Boolean result = false;
+
+        IFlowService flowService =
+                (IFlowService)getContext().getAttributes().
+                get(IFlowService.class.getCanonicalName());
+
+        if (flowService == null) {
+	    log.debug("ONOS Flow Service not found");
+	    return result;
+	}
+
+	// Extract the arguments
+	String numThreadsStr = (String) getRequestAttributes().get("num-threads");
+	Integer numThreads = new Integer(numThreadsStr);
+	log.debug("Measurement Install Paths Number of Threads " + numThreadsStr);
+
+	// Process the request
+	result = flowService.measurementInstallPaths(numThreads);
+	return result;
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementStorePathFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementStorePathFlowResource.java
new file mode 100644
index 0000000..e68ceb7
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/MeasurementStorePathFlowResource.java
@@ -0,0 +1,65 @@
+package net.floodlightcontroller.flowcache.web;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.flowcache.IFlowService;
+import net.floodlightcontroller.util.FlowId;
+import net.floodlightcontroller.util.FlowPath;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.restlet.resource.Post;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MeasurementStorePathFlowResource extends ServerResource {
+
+    protected static Logger log = LoggerFactory.getLogger(MeasurementStorePathFlowResource.class);
+
+    @Post("json")
+    public FlowId store(String flowJson) {
+	FlowId result = new FlowId();
+
+        IFlowService flowService =
+                (IFlowService)getContext().getAttributes().
+                get(IFlowService.class.getCanonicalName());
+
+        if (flowService == null) {
+	    log.debug("ONOS Flow Service not found");
+            return result;
+	}
+
+	//
+	// Extract the arguments
+	// NOTE: The "flow" is specified in JSON format.
+	//
+	ObjectMapper mapper = new ObjectMapper();
+	String flowPathStr = flowJson;
+	FlowPath flowPath = null;
+	log.debug("Measurement Store Flow Path: " + flowPathStr);
+	try {
+	    flowPath = mapper.readValue(flowPathStr, FlowPath.class);
+	} catch (JsonGenerationException e) {
+	    e.printStackTrace();
+	} catch (JsonMappingException e) {
+	    e.printStackTrace();
+	} catch (IOException e) {
+	    e.printStackTrace();
+	}
+
+	// Process the request
+	if (flowPath != null) {
+	    FlowPath addedFlowPath =
+		flowService.measurementStorePathFlow(flowPath);
+	    if (addedFlowPath == null)
+		result = new FlowId();		// Error: Return empty Flow Id
+	    else
+		result = addedFlowPath.flowId();
+	}
+
+        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/restclient/RestClient.java b/src/main/java/net/floodlightcontroller/restclient/RestClient.java
deleted file mode 100644
index 541b42d..0000000
--- a/src/main/java/net/floodlightcontroller/restclient/RestClient.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package net.floodlightcontroller.restclient;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
-import net.sf.json.JSONSerializer;
-
-
-public class RestClient {
-
-	public static void get (String str) {
-		
-		if (str == null)
-			return;
-	
-		try {
-	 
-			URL url = new URL(str);
-			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
-			conn.setRequestMethod("GET");
-			conn.setRequestProperty("Accept", "application/json");
-	 
-			if (conn.getResponseCode() != 200) {
-				throw new RuntimeException("Failed : HTTP error code : "
-						+ conn.getResponseCode());
-			}
-	 
-			if (conn.getContentType().equals("application/json")) 
-			{	}else{
-				System.out.print("The content received is not json format!");				
-			}		
-			
-		 BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream()))); 
-		 StringBuffer res = new StringBuffer();
-		 String line;
-		 while ((line = br.readLine()) != null) {
-			 	res.append(line);
-		 	}	   
-		 
-		 String res2=res.toString().replaceAll("\"", "'");
-		 JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(res2);  
-		 JSONArray rib_json_array = jsonObj.getJSONArray("rib");
-		 String router_id = jsonObj.getString("router-id");
-		       
-		 int size = rib_json_array.size();
-		 System.out.print("size:"+size+"\n");
-		 for (int j = 0; j < size; j++) {
-        JSONObject second_json_object = rib_json_array.getJSONObject(j);
-        String prefix = second_json_object.getString("prefix");
-        String nexthop = second_json_object.getString("nexthop");
-              			
-     		//insert each rib entry into the local rib;
-        RestClient.post("http://127.0.0.1:8090/wm/bgp/"+router_id+"/"+prefix+"/"+nexthop);
-          
-        
-        
-		 }
-		 br.close();
-		 conn.disconnect();
-
-		} catch (MalformedURLException e) {
-
-			e.printStackTrace();
-
-		} catch (IOException e) {
-
-			e.printStackTrace();
-
-		}
-	}
-	
-public static void post (String str) {
-		
-		if (str == null)
-			return;
-	
-		try {
-	 
-			URL url = new URL(str);
-			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
-			conn.setDoOutput(true);
-			conn.setRequestMethod("POST");
-			conn.setRequestProperty("Content-Type", "application/json");		
-	 
-			if (conn.getResponseCode() != 200) {
-				throw new RuntimeException("Failed : HTTP error code : "
-						+ conn.getResponseCode());
-			}
-					
-			conn.disconnect();
-
-		} catch (MalformedURLException e) {
-
-			e.printStackTrace();
-
-		} catch (IOException e) {
-
-			e.printStackTrace();
-
-		}
-	}
-	
-
-public static void delete (String str) {
-	
-	if (str == null)
-		return;
-
-	try {
- 
-		URL url = new URL(str);
-		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
-		conn.setRequestMethod("DELETE");
-		conn.setRequestProperty("Accept", "application/json");
-		
- 
-		if (conn.getResponseCode() != 200) {
-			throw new RuntimeException("Failed : HTTP error code : "
-					+ conn.getResponseCode());
-		}
- 
-		conn.disconnect();
-
-	} catch (MalformedURLException e) {
-
-		e.printStackTrace();
-
-	} catch (IOException e) {
-
-		e.printStackTrace();
-
-	}
-}
-	
-	
-}
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/src/main/java/net/onrc/onos/registry/controller/IControllerRegistryService.java b/src/main/java/net/onrc/onos/registry/controller/IControllerRegistryService.java
index dadee65..21da47e 100644
--- a/src/main/java/net/onrc/onos/registry/controller/IControllerRegistryService.java
+++ b/src/main/java/net/onrc/onos/registry/controller/IControllerRegistryService.java
@@ -115,4 +115,7 @@
 	 * @return Collection of dpids
 	 */
 	public Collection<Long> getSwitchesControlledByController(String controllerId);
+	
+	public IdBlock allocateUniqueIdBlock();
+	
 }
diff --git a/src/main/java/net/onrc/onos/registry/controller/IdBlock.java b/src/main/java/net/onrc/onos/registry/controller/IdBlock.java
new file mode 100644
index 0000000..64b2af8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/registry/controller/IdBlock.java
@@ -0,0 +1,31 @@
+package net.onrc.onos.registry.controller;
+
+public class IdBlock {
+	private long start;
+	private long end;
+	private long size;
+	
+	public IdBlock(long start, long end, long size) {
+		this.start = start;
+		this.end = end;
+		this.size = size;
+	}
+	
+	public long getStart() {
+		return start;
+	}
+
+	public long getEnd() {
+		return end;
+	}
+
+	public long getSize() {
+		return size;
+	}
+	
+	@Override
+	public String toString() {
+		return "IdBlock [start=" + start + ", end=" + end + ", size=" + size
+				+ "]";
+	}
+}
diff --git a/src/main/java/net/onrc/onos/registry/controller/StandaloneRegistry.java b/src/main/java/net/onrc/onos/registry/controller/StandaloneRegistry.java
index 2c220fd..e48c519 100644
--- a/src/main/java/net/onrc/onos/registry/controller/StandaloneRegistry.java
+++ b/src/main/java/net/onrc/onos/registry/controller/StandaloneRegistry.java
@@ -115,6 +115,12 @@
 			String controllerId) {
 		throw new RuntimeException("Not yet implemented");
 	}
+	
+	@Override
+	public IdBlock allocateUniqueIdBlock(){
+		//XXX Not exactly unique...
+		return new IdBlock(0L, 0x10000000L, 0x10000000L);
+	}
 
 	@Override
 	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
diff --git a/src/main/java/net/onrc/onos/registry/controller/ZookeeperRegistry.java b/src/main/java/net/onrc/onos/registry/controller/ZookeeperRegistry.java
index 76b7ebd..82259a9 100644
--- a/src/main/java/net/onrc/onos/registry/controller/ZookeeperRegistry.java
+++ b/src/main/java/net/onrc/onos/registry/controller/ZookeeperRegistry.java
@@ -24,6 +24,8 @@
 import com.netflix.curator.RetryPolicy;
 import com.netflix.curator.framework.CuratorFramework;
 import com.netflix.curator.framework.CuratorFrameworkFactory;
+import com.netflix.curator.framework.recipes.atomic.AtomicValue;
+import com.netflix.curator.framework.recipes.atomic.DistributedAtomicLong;
 import com.netflix.curator.framework.recipes.cache.ChildData;
 import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
 import com.netflix.curator.framework.recipes.cache.PathChildrenCache.StartMode;
@@ -33,6 +35,7 @@
 import com.netflix.curator.framework.recipes.leader.LeaderLatchEvent;
 import com.netflix.curator.framework.recipes.leader.LeaderLatchListener;
 import com.netflix.curator.retry.ExponentialBackoffRetry;
+import com.netflix.curator.retry.RetryOneTime;
 import com.netflix.curator.x.discovery.ServiceCache;
 import com.netflix.curator.x.discovery.ServiceDiscovery;
 import com.netflix.curator.x.discovery.ServiceDiscoveryBuilder;
@@ -67,6 +70,10 @@
 	protected ConcurrentHashMap<String, SwitchLeadershipData> switches;
 	protected Map<String, PathChildrenCache> switchPathCaches;
 	
+	private final String ID_COUNTER_PATH = "/flowidcounter";
+	private final Long ID_BLOCK_SIZE = 0x100000000L;
+	protected DistributedAtomicLong distributedIdCounter;
+	
 	//Zookeeper performance-related configuration
 	protected static final int sessionTimeout = 5000;
 	protected static final int connectionTimeout = 7000;
@@ -372,6 +379,21 @@
 		return data;
 	}
 	
+	public IdBlock allocateUniqueIdBlock(){
+		try {
+			AtomicValue<Long> result = null;
+			do {
+				result = distributedIdCounter.add(ID_BLOCK_SIZE);
+			} while (result == null || !result.succeeded());
+			
+			return new IdBlock(result.preValue(), result.postValue() - 1, ID_BLOCK_SIZE);
+		} catch (Exception e) {
+			log.error("Error allocating ID block");
+		} 
+		
+		return null;
+	}
+	
 	/*
 	 * IFloodlightModule
 	 */
@@ -427,6 +449,10 @@
 		client.start();
 		client = client.usingNamespace(namespace);
 
+		distributedIdCounter = new DistributedAtomicLong(
+				client, 
+				ID_COUNTER_PATH, 
+				new RetryOneTime(100));
 		
 		switchCache = new PathChildrenCache(client, switchLatchesPath, true);
 		switchCache.getListenable().addListener(switchPathCacheListener);
diff --git a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
index 99ca4c8..b4b4f27 100644
--- a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
+++ b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
@@ -26,6 +26,6 @@
 net.floodlightcontroller.onoslistener.OnosPublisher
 net.floodlightcontroller.flowcache.FlowManager
 net.floodlightcontroller.routing.TopoRouteService
+net.floodlightcontroller.bgproute.BgpRoute
 net.onrc.onos.registry.controller.ZookeeperRegistry
 net.onrc.onos.registry.controller.StandaloneRegistry
-
diff --git a/start-onos.sh b/start-onos.sh
index 77accd0..0a59701 100755
--- a/start-onos.sh
+++ b/start-onos.sh
@@ -82,8 +82,23 @@
   echo "Starting ONOS controller ..."
   echo 
   #java ${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -jar ${FL_JAR} -cf ${FL_HOME}/onos.properties > /dev/null 2>&1 &
-  java ${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -cp ${CLASSPATH} ${MAIN_CLASS} -cf ${FL_HOME}/onos.properties > /dev/null 2>&1 &
+  #java ${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -cp ${CLASSPATH} ${MAIN_CLASS} -cf ${FL_HOME}/onos.properties > /dev/n
 
+  mvn exec:exec -Dexec.executable="java" -Dexec.args="${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -cp %classpath ${MAIN_CLASS} -cf ${FL_HOME}/onos.properties" > ${LOGDIR}/onos.stdout 2>${LOGDIR}/onos.stderr &
+
+  echo "Waiting for ONOS to start..."
+  COUNT=0
+  ESTATE=0
+  while [ "$COUNT" != "10" ]; do
+    COUNT=$((COUNT + 1))
+    n=`jps -l |grep "${MAIN_CLASS}" | wc -l`
+    if [ "$n" -ge "1" ]; then
+      exit 0
+    fi
+    sleep $COUNT
+  done
+  echo "Timed out"
+  exit 1
 
 #  echo "java ${JVM_OPTS} -Dlogback.configurationFile=${FL_LOGBACK} -jar ${FL_JAR} -cf ./onos.properties > /dev/null 2>&1 &"
 #  sudo -b /usr/sbin/tcpdump -n -i eth0 -s0 -w ${PCAP_LOG} 'tcp port 6633' > /dev/null  2>&1
@@ -128,7 +143,7 @@
     start 
     ;;
   startifdown)
-    n=`jps -l |grep "net.floodlightcontroller.core.Main" | wc -l`
+    n=`jps -l |grep "${MAIN_CLASS}" | wc -l`
     if [ $n == 0 ]; then
       start
     else 
@@ -142,7 +157,7 @@
     deldb
     ;;
   status)
-    n=`jps -l |grep "net.floodlightcontroller.core.Main" | wc -l`
+    n=`jps -l |grep "${MAIN_CLASS}" | wc -l`
     echo "$n instance of onos running"
     ;;
   *)
diff --git a/titan/gremlin.sh b/titan/gremlin.sh
index c7155ac..aa9c513 100755
--- a/titan/gremlin.sh
+++ b/titan/gremlin.sh
@@ -1,6 +1,8 @@
 #!/bin/bash
 
-CP=$( echo `dirname $0`/../lib/*.jar `dirname $0`/../lib/titan/*.jar . | sed 's/ /:/g')
+ONOS_DIR="`dirname $0`/.."
+#CP=$( echo `dirname $0`/../lib/*.jar `dirname $0`/../lib/titan/*.jar . | sed 's/ /:/g')
+CP=`mvn -f ${ONOS_DIR}/pom.xml dependency:build-classpath -Dmdep.outputFile=/dev/stdout -l /dev/stderr`
 
 # Find Java 
 if [ "$JAVA_HOME" = "" ] ; then
diff --git a/titan/listNotUpdated b/titan/listNotUpdated
new file mode 100644
index 0000000..a44c458
--- /dev/null
+++ b/titan/listNotUpdated
@@ -0,0 +1,4 @@
+g.stopTransaction(SUCCESS)
+g.V('type', 'flow_entry').each{
+if (it.switch_state.equals("FE_SWITCH_NOT_UPDATED")) println it.map.next()
+}
\ No newline at end of file
diff --git a/web/clear_flow.py b/web/clear_flow.py
index 50678e2..db70d40 100755
--- a/web/clear_flow.py
+++ b/web/clear_flow.py
@@ -51,6 +51,7 @@
   usage_msg = usage_msg + "    Arguments:\n"
   usage_msg = usage_msg + "        <begin-flow-id> <end-flow-id>      Clear all flows in the flow ID range\n"
   usage_msg = usage_msg + "        <flow-id>                          Clear a single flow with the flow ID\n"
+  usage_msg = usage_msg + "        all                                Clear all flows\n"
 
   # app.debug = False;
 
@@ -63,14 +64,18 @@
   if len(sys.argv) < 2:
     log_error(usage_msg)
     exit(1)
-  begin_flow_id = int(sys.argv[1], 0)
-  if len(sys.argv) >= 3:
-    end_flow_id = int(sys.argv[2], 0)
-  else:
-    end_flow_id = begin_flow_id
 
-  # Do the work
-  flow_id = begin_flow_id
-  while flow_id <= end_flow_id:
-    clear_flow_path(flow_id)
-    flow_id = flow_id + 1
+  if (sys.argv[1] == "all"):
+    clear_flow_path(sys.argv[1])
+  else:
+    begin_flow_id = int(sys.argv[1], 0)
+    if len(sys.argv) >= 3:
+      end_flow_id = int(sys.argv[2], 0)
+    else:
+      end_flow_id = begin_flow_id
+
+    # Do the work
+    flow_id = begin_flow_id
+    while flow_id <= end_flow_id:
+      clear_flow_path(flow_id)
+      flow_id = flow_id + 1
diff --git a/web/delete_flow.py b/web/delete_flow.py
index ff4caff..fff9319 100755
--- a/web/delete_flow.py
+++ b/web/delete_flow.py
@@ -51,6 +51,7 @@
   usage_msg = usage_msg + "    Arguments:\n"
   usage_msg = usage_msg + "        <begin-flow-id> <end-flow-id>      Delete all flows in the flow ID range\n"
   usage_msg = usage_msg + "        <flow-id>                          Delete a single flow with the flow ID\n"
+  usage_msg = usage_msg + "        all                                Delete all flows\n"
 
   # app.debug = False;
 
@@ -63,14 +64,18 @@
   if len(sys.argv) < 2:
     log_error(usage_msg)
     exit(1)
-  begin_flow_id = int(sys.argv[1], 0)
-  if len(sys.argv) >= 3:
-    end_flow_id = int(sys.argv[2], 0)
-  else:
-    end_flow_id = begin_flow_id
 
-  # Do the work
-  flow_id = begin_flow_id
-  while flow_id <= end_flow_id:
-    delete_flow_path(flow_id)
-    flow_id = flow_id + 1
+  if (sys.argv[1] == "all"):
+    delete_flow_path(sys.argv[1])
+  else:
+    begin_flow_id = int(sys.argv[1], 0)
+    if len(sys.argv) >= 3:
+      end_flow_id = int(sys.argv[2], 0)
+    else:
+      end_flow_id = begin_flow_id
+
+    # Do the work
+    flow_id = begin_flow_id
+    while flow_id <= end_flow_id:
+      delete_flow_path(flow_id)
+      flow_id = flow_id + 1
diff --git a/web/measurement_clear_all_paths.py b/web/measurement_clear_all_paths.py
new file mode 100755
index 0000000..5bb73c5
--- /dev/null
+++ b/web/measurement_clear_all_paths.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-clear-all-paths/json")
+def measurement_clear_all_paths():
+  command = "curl -s \"http://%s:%s/wm/flow/measurement-clear-all-paths/json\"" % (ControllerIP, ControllerPort)
+  debug("measurement_clear_all_paths %s" % command)
+  result = os.popen(command).read()
+  debug("result %s" % result)
+  # parsedResult = json.loads(result)
+  # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+  usage_msg = "Clear the paths that have been stored for measurement purpose\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_clear_all_paths()
diff --git a/web/measurement_get_install_paths_time_nsec.py b/web/measurement_get_install_paths_time_nsec.py
new file mode 100755
index 0000000..d64dc49
--- /dev/null
+++ b/web/measurement_get_install_paths_time_nsec.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-install-paths-time-nsec/json")
+def measurement_get_install_paths_time_nsec():
+  command = "curl -s \"http://%s:%s/wm/flow/measurement-get-install-paths-time-nsec/json\"" % (ControllerIP, ControllerPort)
+  debug("measurement_get_install_paths_time_nsec %s" % command)
+  result = os.popen(command).read()
+  print '%s nsec' % (result)
+  # parsedResult = json.loads(result)
+  # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+  usage_msg = "Get the measured time to install the stored flow paths\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_install_paths_time_nsec()
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_install_paths.py b/web/measurement_install_paths.py
new file mode 100755
index 0000000..d99070e
--- /dev/null
+++ b/web/measurement_install_paths.py
@@ -0,0 +1,67 @@
+#! /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-install-paths/<num-threads>/json")
+def measurement_install_paths(num_threads):
+  command = "curl -s \"http://%s:%s/wm/flow/measurement-install-paths/%s/json\"" % (ControllerIP, ControllerPort, num_threads)
+  debug("measurement_install_paths %s" % command)
+  result = os.popen(command).read()
+  debug("result %s" % result)
+  # parsedResult = json.loads(result)
+  # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+  usage_msg = "Install flow paths and start measurements\n"
+  usage_msg = usage_msg + "Usage: %s <num-threads>\n" % (sys.argv[0])
+  usage_msg = usage_msg + "\n"
+  usage_msg = usage_msg + "    Arguments:\n"
+  usage_msg = usage_msg + "        <num-threads>      Number of threads to use to install the flows\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
+  if len(sys.argv) < 2:
+    log_error(usage_msg)
+    exit(1)
+  num_threads = int(sys.argv[1], 0)
+
+  # Do the work
+  measurement_install_paths(num_threads)
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)
diff --git a/web/measurement_store_flow.py b/web/measurement_store_flow.py
new file mode 100755
index 0000000..637ab3e
--- /dev/null
+++ b/web/measurement_store_flow.py
@@ -0,0 +1,447 @@
+#! /usr/bin/env python
+# -*- Mode: python; py-indent-offset: 4; tab-width: 8; indent-tabs-mode: t; -*-
+
+import copy
+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
+
+## Global Var ##
+ControllerIP = "127.0.0.1"
+ControllerPort = 8080
+ReadFromFile = ""
+
+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)
+
+def measurement_store_path_flow(flow_path):
+  flow_path_json = json.dumps(flow_path)
+
+  try:
+    command = "curl -s -H 'Content-Type: application/json' -d '%s' http://%s:%s/wm/flow/measurement-store-path/json" % (flow_path_json, ControllerIP, ControllerPort)
+    debug("measurement_store_path_flow %s" % command)
+    result = os.popen(command).read()
+    debug("result %s" % result)
+    # parsedResult = json.loads(result)
+    # debug("parsed %s" % parsedResult)
+  except:
+    log_error("Controller IF has issue")
+    exit(1)
+
+def extract_flow_args(my_args):
+  # Check the arguments
+  if len(my_args) < 6:
+    log_error(usage_msg)
+    exit(1)
+
+  # Extract the mandatory arguments
+  my_flow_id = my_args[0]
+  my_installer_id = my_args[1]
+  my_src_dpid = my_args[2]
+  my_src_port = my_args[3]
+  my_dst_dpid = my_args[4]
+  my_dst_port = my_args[5]
+
+  #
+  # Extract the "match" and "action" arguments
+  #
+  match = {}
+  matchInPortEnabled = True		# NOTE: Enabled by default
+  actions = []
+  actionOutputEnabled = True		# NOTE: Enabled by default
+  idx = 6
+  while idx < len(my_args):
+    action = {}
+    arg1 = my_args[idx]
+    idx = idx + 1
+    # Extract the second argument
+    if idx >= len(my_args):
+      error_arg = "ERROR: Missing or invalid '" + arg1 + "' argument"
+      log_error(error_arg)
+      log_error(usage_msg)
+      exit(1)
+    arg2 = my_args[idx]
+    idx = idx + 1
+
+    if arg1 == "matchInPort":
+      # Just mark whether inPort matching is enabled
+      matchInPortEnabled = arg2 in ['True', 'true']
+      # inPort = {}
+      # inPort['value'] = int(arg2, 0)
+      # match['inPort'] = inPort
+      ## match['matchInPort'] = True
+    elif arg1 == "matchSrcMac":
+      srcMac = {}
+      srcMac['value'] = arg2
+      match['srcMac'] = srcMac
+      # match['matchSrcMac'] = True
+    elif arg1 == "matchDstMac":
+      dstMac = {}
+      dstMac['value'] = arg2
+      match['dstMac'] = dstMac
+      # match['matchDstMac'] = True
+    elif arg1 == "matchVlanId":
+      match['vlanId'] = int(arg2, 0)
+      # match['matchVlanId'] = True
+    elif arg1 == "matchVlanPriority":
+      match['vlanPriority'] = int(arg2, 0)
+      # match['matchVlanPriority'] = True
+    elif arg1 == "matchEthernetFrameType":
+      match['ethernetFrameType'] = int(arg2, 0)
+      # match['matchEthernetFrameType'] = True
+    elif arg1 == "matchIpToS":
+      match['ipToS'] = int(arg2, 0)
+      # match['matchIpToS'] = True
+    elif arg1 == "matchIpProto":
+      match['ipProto'] = int(arg2, 0)
+      # match['matchIpProto'] = True
+    elif arg1 == "matchSrcIPv4Net":
+      srcIPv4Net = {}
+      srcIPv4Net['value'] = arg2
+      match['srcIPv4Net'] = srcIPv4Net
+      # match['matchSrcIPv4Net'] = True
+    elif arg1 == "matchDstIPv4Net":
+      dstIPv4Net = {}
+      dstIPv4Net['value'] = arg2
+      match['dstIPv4Net'] = dstIPv4Net
+      # match['matchDstIPv4Net'] = True
+    elif arg1 == "matchSrcTcpUdpPort":
+      match['srcTcpUdpPort'] = int(arg2, 0)
+      # match['matchSrcTcpUdpPort'] = True
+    elif arg1 == "matchDstTcpUdpPort":
+      match['dstTcpUdpPort'] = int(arg2, 0)
+      # match['matchDstTcpUdpPort'] = True
+    elif arg1 == "actionOutput":
+      # Just mark whether ACTION_OUTPUT action is enabled
+      actionOutputEnabled = arg2 in ['True', 'true']
+      #
+      # TODO: Complete the implementation for ACTION_OUTPUT
+      #   actionOutput = {}
+      #   outPort = {}
+      #   outPort['value'] = int(arg2, 0)
+      #   actionOutput['port'] = outPort
+      #   actionOutput['maxLen'] = int(arg3, 0)
+      #   action['actionOutput'] = actionOutput
+      #   # action['actionType'] = 'ACTION_OUTPUT'
+      #   actions.append(action)
+      #
+    elif arg1 == "actionSetVlanId":
+      vlanId = {}
+      vlanId['vlanId'] = int(arg2, 0)
+      action['actionSetVlanId'] = vlanId
+      # action['actionType'] = 'ACTION_SET_VLAN_VID'
+      actions.append(copy.deepcopy(action))
+    elif arg1 == "actionSetVlanPriority":
+      vlanPriority = {}
+      vlanPriority['vlanPriority'] = int(arg2, 0)
+      action['actionSetVlanPriority'] = vlanPriority
+      # action['actionType'] = 'ACTION_SET_VLAN_PCP'
+      actions.append(copy.deepcopy(action))
+    elif arg1 == "actionSetIpToS":
+      ipToS = {}
+      ipToS['ipToS'] = int(arg2, 0)
+      action['actionSetIpToS'] = ipToS
+      # action['actionType'] = 'ACTION_SET_NW_TOS'
+      actions.append(copy.deepcopy(action))
+    elif arg1 == "actionSetTcpUdpSrcPort":
+      tcpUdpSrcPort = {}
+      tcpUdpSrcPort['port'] = int(arg2, 0)
+      action['actionSetTcpUdpSrcPort'] = tcpUdpSrcPort
+      # action['actionType'] = 'ACTION_SET_TP_SRC'
+      actions.append(copy.deepcopy(action))
+    elif arg1 == "actionSetTcpUdpDstPort":
+      tcpUdpDstPort = {}
+      tcpUdpDstPort['port'] = int(arg2, 0)
+      action['actionSetTcpUdpDstPort'] = tcpUdpDstPort
+      # action['actionType'] = 'ACTION_SET_TP_DST'
+      actions.append(copy.deepcopy(action))
+    elif arg1 == "actionStripVlan":
+      stripVlan = {}
+      stripVlan['stripVlan'] = arg2 in ['True', 'true']
+      action['actionStripVlan'] = stripVlan
+      # action['actionType'] = 'ACTION_STRIP_VLAN'
+      actions.append(copy.deepcopy(action))
+    elif arg1 == "actionSetEthernetSrcAddr":
+      ethernetSrcAddr = {}
+      ethernetSrcAddr['value'] = arg2
+      setEthernetSrcAddr = {}
+      setEthernetSrcAddr['addr'] = ethernetSrcAddr
+      action['actionSetEthernetSrcAddr'] = setEthernetSrcAddr
+      # action['actionType'] = 'ACTION_SET_DL_SRC'
+      actions.append(copy.deepcopy(action))
+    elif arg1 == "actionSetEthernetDstAddr":
+      ethernetDstAddr = {}
+      ethernetDstAddr['value'] = arg2
+      setEthernetDstAddr = {}
+      setEthernetDstAddr['addr'] = ethernetDstAddr
+      action['actionSetEthernetDstAddr'] = setEthernetDstAddr
+      # action['actionType'] = 'ACTION_SET_DL_DST'
+      actions.append(copy.deepcopy(action))
+    elif arg1 == "actionSetIPv4SrcAddr":
+      IPv4SrcAddr = {}
+      IPv4SrcAddr['value'] = arg2
+      setIPv4SrcAddr = {}
+      setIPv4SrcAddr['addr'] = IPv4SrcAddr
+      action['actionSetIPv4SrcAddr'] = setIPv4SrcAddr
+      # action['actionType'] = 'ACTION_SET_NW_SRC'
+      actions.append(copy.deepcopy(action))
+    elif arg1 == "actionSetIPv4DstAddr":
+      IPv4DstAddr = {}
+      IPv4DstAddr['value'] = arg2
+      setIPv4DstAddr = {}
+      setIPv4DstAddr['addr'] = IPv4DstAddr
+      action['actionSetIPv4DstAddr'] = setIPv4DstAddr
+      # action['actionType'] = 'ACTION_SET_NW_DST'
+      actions.append(copy.deepcopy(action))
+    elif arg1 == "actionEnqueue":
+      # TODO: Implement ACTION_ENQUEUE
+      actionEnqueue = {}
+      #   actionEnqueue['queueId'] = int(arg2, 0)
+      #   enqueuePort = {}
+      #   enqueuePort['value'] = int(arg3, 0)
+      #   actionEnqueue['port'] = enqueuePort
+      #   action['actionEnqueue'] = actionEnqueue
+      #   # action['actionType'] = 'ACTION_ENQUEUE'
+      #   actions.append(copy.deepcopy(action))
+      #
+    else:
+      log_error("ERROR: Unknown argument '%s'" % (arg1))
+      log_error(usage_msg)
+      exit(1)
+
+  return {
+    'my_flow_id' : my_flow_id,
+    'my_installer_id' : my_installer_id,
+    'my_src_dpid' : my_src_dpid,
+    'my_src_port' : my_src_port,
+    'my_dst_dpid' : my_dst_dpid,
+    'my_dst_port' : my_dst_port,
+    'match' : match,
+    'matchInPortEnabled' : matchInPortEnabled,
+    'actions' : actions,
+    'actionOutputEnabled' : actionOutputEnabled
+    }
+
+def compute_flow_path(parsed_args, data_path):
+
+  my_flow_id = parsed_args['my_flow_id']
+  my_installer_id = parsed_args['my_installer_id']
+  match = parsed_args['match']
+  matchInPortEnabled = parsed_args['matchInPortEnabled']
+  actions = parsed_args['actions']
+  actionOutputEnabled = parsed_args['actionOutputEnabled']
+  my_data_path = copy.deepcopy(data_path)
+
+  flow_id = {}
+  flow_id['value'] = my_flow_id
+  installer_id = {}
+  installer_id['value'] = my_installer_id
+
+  flow_path = {}
+  flow_path['flowId'] = flow_id
+  flow_path['installerId'] = installer_id
+
+  if (len(match) > 0):
+    flow_path['flowEntryMatch'] = copy.deepcopy(match)
+
+  #
+  # Add the match conditions to each flow entry
+  #
+  if (len(match) > 0) or matchInPortEnabled:
+    idx = 0
+    while idx < len(my_data_path['flowEntries']):
+      if matchInPortEnabled:
+	inPort = my_data_path['flowEntries'][idx]['inPort']
+	match['inPort'] = copy.deepcopy(inPort)
+	# match['matchInPort'] = True
+      my_data_path['flowEntries'][idx]['flowEntryMatch'] = copy.deepcopy(match)
+      idx = idx + 1
+
+  #
+  # Set the actions for each flow entry
+  # NOTE: The actions from the command line are aplied
+  # ONLY to the first flow entry.
+  #
+  # If ACTION_OUTPUT action is enabled, then apply it
+  # to each flow entry.
+  #
+  if (len(actions) > 0) or actionOutputEnabled:
+    idx = 0
+    while idx < len(my_data_path['flowEntries']):
+      if idx > 0:
+	actions = []	# Reset the actions for all but first entry
+      action = {}
+      outPort = my_data_path['flowEntries'][idx]['outPort']
+      actionOutput = {}
+      actionOutput['port'] = copy.deepcopy(outPort)
+      # actionOutput['maxLen'] = 0	# TODO: not used for now
+      action['actionOutput'] = copy.deepcopy(actionOutput)
+      # action['actionType'] = 'ACTION_OUTPUT'
+      actions.append(copy.deepcopy(action))
+
+      my_data_path['flowEntries'][idx]['flowEntryActions'] = copy.deepcopy(actions)
+      idx = idx + 1
+
+
+  flow_path['dataPath'] = my_data_path
+  debug("Flow Path: %s" % flow_path)
+  return flow_path
+
+def measurement_store_paths(parsed_args):
+  idx = 0
+  while idx < len(parsed_args):
+    data_path = {}
+    src_dpid = {}
+    src_port = {}
+    dst_dpid = {}
+    dst_port = {}
+    src_switch_port = {}
+    dst_switch_port = {}
+    flow_entries = []
+
+    src_dpid['value'] = parsed_args[idx]['my_src_dpid']
+    src_port['value'] = parsed_args[idx]['my_src_port']
+    dst_dpid['value'] = parsed_args[idx]['my_dst_dpid']
+    dst_port['value'] = parsed_args[idx]['my_dst_port']
+    src_switch_port['dpid'] = src_dpid
+    src_switch_port['port'] = src_port
+    dst_switch_port['dpid'] = dst_dpid
+    dst_switch_port['port'] = dst_port
+
+    data_path['srcPort'] = copy.deepcopy(src_switch_port)
+    data_path['dstPort'] = copy.deepcopy(dst_switch_port)
+    data_path['flowEntries'] = copy.deepcopy(flow_entries)
+
+    #
+    # XXX: Explicitly disable the InPort matching, and
+    # the Output action, because they get in the way
+    # during the compute_flow_path() processing.
+    #
+    parsed_args[idx]['matchInPortEnabled'] = False
+    parsed_args[idx]['actionOutputEnabled'] = False
+
+    flow_path = compute_flow_path(parsed_args[idx], data_path)
+    measurement_store_path_flow(flow_path)
+
+    idx = idx + 1
+
+
+if __name__ == "__main__":
+  usage_msg = "Store Flow Paths into ONOS for measurement purpose.\n"
+  usage_msg = usage_msg + "\n"
+  usage_msg = usage_msg + "Usage: %s [Flags] <flow-id> <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port> [Match Conditions] [Actions]\n" % (sys.argv[0])
+  usage_msg = usage_msg + "\n"
+  usage_msg = usage_msg + "    Flags:\n"
+  usage_msg = usage_msg + "        -f <filename>     Read the flow(s) to install from a file\n"
+  usage_msg = usage_msg + "                          File format: one line per flow starting with <flow-id>\n"
+  usage_msg = usage_msg + "\n"
+  usage_msg = usage_msg + "    Match Conditions:\n"
+  usage_msg = usage_msg + "        matchInPort <True|False> (default to True)\n"
+  usage_msg = usage_msg + "        matchSrcMac <source MAC address>\n"
+  usage_msg = usage_msg + "        matchDstMac <destination MAC address>\n"
+  usage_msg = usage_msg + "        matchSrcIPv4Net <source IPv4 network address>\n"
+  usage_msg = usage_msg + "        matchDstIPv4Net <destination IPv4 network address>\n"
+  usage_msg = usage_msg + "        matchEthernetFrameType <Ethernet frame type>\n"
+  usage_msg = usage_msg + "\n"
+  usage_msg = usage_msg + "    Match Conditions (not implemented yet):\n"
+  usage_msg = usage_msg + "        matchVlanId <VLAN ID>\n"
+  usage_msg = usage_msg + "        matchVlanPriority <VLAN priority>\n"
+  usage_msg = usage_msg + "        matchIpToS <IP ToS (DSCP field, 6 bits)>\n"
+  usage_msg = usage_msg + "        matchIpProto <IP protocol>\n"
+  usage_msg = usage_msg + "        matchSrcTcpUdpPort <source TCP/UDP port>\n"
+  usage_msg = usage_msg + "        matchDstTcpUdpPort <destination TCP/UDP port>\n"
+  usage_msg = usage_msg + "\n"
+  usage_msg = usage_msg + "    Actions:\n"
+  usage_msg = usage_msg + "        actionOutput <True|False> (default to True)\n"
+  usage_msg = usage_msg + "        actionSetEthernetSrcAddr <source MAC address>\n"
+  usage_msg = usage_msg + "        actionSetEthernetDstAddr <destination MAC address>\n"
+  usage_msg = usage_msg + "        actionSetIPv4SrcAddr <source IPv4 address>\n"
+  usage_msg = usage_msg + "        actionSetIPv4DstAddr <destination IPv4 address>\n"
+  usage_msg = usage_msg + "\n"
+  usage_msg = usage_msg + "    Actions (not implemented yet):\n"
+  usage_msg = usage_msg + "        actionSetVlanId <VLAN ID>\n"
+  usage_msg = usage_msg + "        actionSetVlanPriority <VLAN priority>\n"
+  usage_msg = usage_msg + "        actionSetIpToS <IP ToS (DSCP field, 6 bits)>\n"
+  usage_msg = usage_msg + "        actionSetTcpUdpSrcPort <source TCP/UDP port>\n"
+  usage_msg = usage_msg + "        actionSetTcpUdpDstPort <destination TCP/UDP port>\n"
+  usage_msg = usage_msg + "        actionStripVlan <True|False>\n"
+  usage_msg = usage_msg + "        actionEnqueue <dummy argument>\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 the flags
+  #
+  start_argv_index = 1
+  idx = 1
+  while idx < len(sys.argv):
+    arg1 = sys.argv[idx]
+    idx = idx + 1
+    if arg1 == "-f":
+      if idx >= len(sys.argv):
+	error_arg = "ERROR: Missing or invalid '" + arg1 + "' argument"
+	log_error(error_arg)
+	log_error(usage_msg)
+	exit(1)
+      ReadFromFile = sys.argv[idx]
+      idx = idx + 1
+      start_argv_index = idx
+    else:
+      break;
+
+  #
+  # Read the arguments from a file or from the remaining command line options
+  #
+  my_lines = []
+  if len(ReadFromFile) > 0:
+    f = open(ReadFromFile, "rt")
+    my_line = f.readline()
+    while my_line:
+      if len(my_line.rstrip()) > 0 and my_line[0] != "#":
+	my_token_line = my_line.rstrip().split()
+	my_lines.append(my_token_line)
+      my_line = f.readline()
+  else:
+    my_lines.append(copy.deepcopy(sys.argv[start_argv_index:]))
+
+  #
+  # Initialization
+  #
+  last_data_paths = []
+  parsed_args = []
+  idx = 0
+  while idx < len(my_lines):
+    last_data_path = []
+    last_data_paths.append(copy.deepcopy(last_data_path))
+    #
+    # Parse the flow arguments
+    #
+    my_args = my_lines[idx]
+    parsed_args.append(copy.deepcopy(extract_flow_args(my_args)))
+
+    idx = idx + 1
+
+  #
+  measurement_store_paths(parsed_args)
diff --git a/web/shortest_path.py b/web/shortest_path.py
index b379a82..0f23bf4 100755
--- a/web/shortest_path.py
+++ b/web/shortest_path.py
@@ -20,7 +20,7 @@
 ControllerIP="127.0.0.1"
 ControllerPort=8080
 
-DEBUG=1
+DEBUG=0
 pp = pprint.PrettyPrinter(indent=4)
 
 app = Flask(__name__)