Merge branch 'master' of https://github.com/OPENNETWORKINGLAB/ONOS

Conflicts:
	test-network/mininet/dev_network_edge_2.py
diff --git a/README.md b/README.md
index 495a2ed..b71b9aa 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,118 @@
-ONOS
-====
+ONOS (Open Networking Operating System)
+=======================================
 
-Open Networking Operating System
+ONOS (Open Networking Operating System) is an experimental distributed
+SDN OS. Currently, it is under active development. ONOS was announced
+and demonstrated at ONS'13.
 
-BELOW TO BE WRITTEN IN DETAIL
+Steps to download and setup a development Virtual Machine
+==========================================
 
+http://wiki.onlab.us/display/Eng/ONOS+Development+VM
 
 Building ONOS
 -------------
 
-0. Install custom jars and dependencies (Only need to be run only once)
+0. Install custom jars and dependencies (needs to be run only once)
 
     $ ./setup-local-maven.sh
 
-1. Cleanly Build ONOS
+1. Cleanly build ONOS
 
     $ mvn clean
     $ mvn compile
 
-    NOTE: installing maven for the first time may switch java version from 1.7 to 1.6 causing cassandra to not run
+    NOTE: installing maven for the first time may switch java version
+    from 1.7 to 1.6. This might prevent Cassandra to run.
 
 Dependencies
 ------------
 1. Zookeeper
-    Download and install apache-zookeeper-3.4.5: http://zookeeper.apache.org/releases.html
+    Download and install apache-zookeeper-3.4.5:
+    http://zookeeper.apache.org/releases.html
+
+    Edit file (ONOS-INSTALL-DIR)/start-zk.sh and set variable "ZK_DIR"
+    to point to the Zookeeper directory.
+
 2. Cassandra
-    Download and install apache-cassandra-1.2.2: http://cassandra.apache.org/download/
+    Download and install apache-cassandra-1.2.4:
+    http://cassandra.apache.org/download/
 
-Running ONOS
-------------
+    Edit file (ONOS-INSTALL-DIR)/start-cassandra.sh and set variable
+    "CASSANDRA_DIR" to point to the Cassandra directory.
 
-1. Start zookeeper
+Running ONOS with Cassandra as a separate process
+-------------------------------------------------
+[See below for information how to run ONOS with Embedded Cassandra]
+
+1. Start Zookeeper
 
     $ cd (ONOS-INSTALL-DIR)/
+    $ ./start-zk.sh start
 
-    $ ./start-zk.sh
+    ## Confirm Zookeeper is running:
+    $ ./start.zk.sh status
 
-2. Start cassandra
+2. Start Cassandra
 
     $ cd (ONOS-INSTALL-DIR)/
-
     $ ./start-cassandra.sh start
 
-  1. Confirm cassandra is running
-  
-      $ ./start-cassandra.sh status
+    ## Confirm Cassandra is running:
+    $ ./start-cassandra.sh status
 
-3. Start ONOS instance
+3. Start ONOS
 
     $ cd (ONOS-INSTALL-DIR)/
-
     $ ./start-onos.sh start
-    
-4. Start ONOS rest apis
 
+    ## Confirm ONOS is running:
+    $ ./start-onos.sh status
+    
+4. Start ONOS REST API server
+
+    $ cd (ONOS-INSTALL-DIR)/
     $ ./start-rest.sh start
 
+    ## Confirm the REST API server is running:
+    $ ./start-rest.sh status
+
 Running ONOS with Cassandra embedded (Optional)
 -----------------------------------------------
 
 1. Start Zookeeper
 
     $ cd (ONOS-INSTALL-DIR)/
+    $ ./start-zk.sh start
 
-    $ ./zkServer.sh start
+    ## Confirm Zookeeper is running:
+    $ ./start.zk.sh status
     
 2. Start ONOS and Cassandra embedded
 
     $ cd (ONOS-INSTALL_DIR)/
-    
     $ ./start-onos-embedded.sh start
-    
-3. Start ONOS rest apis
 
+    ## Confirm ONOS is running:
+    $ ./start-onos-embedded.sh status
+    
+3. Start ONOS REST API server
+
+    $ cd (ONOS-INSTALL-DIR)/
     $ ./start-rest.sh start
 
+    ## Confirm the REST API server is running:
+    $ ./start-rest.sh status
+
 
 Running in offline mode (Optional)
 ----------------------------------
 
-Maven is used to build and run ONOS. 
-By default, maven tries to reach the repositories.
-To suppress this behavior '-o' option should be given to `mvn` command.
-
-To give additional option to `mvn` commands used in ONOS, 
-use the MVN environment variable.
+Maven is used to build and run ONOS. By default, maven tries to reach
+the repositories. The '-o' option can be given to the 'mvn' command to
+suppress this behavior. The MVN environmental variable can be used to
+set additional options to the 'mvn' command used in ONOS.
 
 * Example: Running in offline mode
 
     $ env MVN="mvn -o" ./start-onos.sh start
-
diff --git a/conf/cassandra-repair.sh b/conf/cassandra-repair.sh
new file mode 100755
index 0000000..2c2638e
--- /dev/null
+++ b/conf/cassandra-repair.sh
@@ -0,0 +1 @@
+<cassandra_dir>/bin/nodetool repair
diff --git a/conf/logback-deployment.xml b/conf/logback-deployment.xml
new file mode 100644
index 0000000..3877f65
--- /dev/null
+++ b/conf/logback-deployment.xml
@@ -0,0 +1,37 @@
+<configuration scan="true" debug="true">
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%level [%logger:%thread] %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+    <file>./onos-logs/onos.ubuntu.log</file>
+    <encoder>
+      <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <appender name="ROLLINGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>./onos-logs/onos.ubuntu.log</file>
+    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+      <!-- Roll over to a new log file every day -->
+      <fileNamePattern>./onos-logs/onos.ubuntu.%d{yyyy-MM-dd}.log</fileNamePattern>
+      <!-- Keep 10 days worth of logs -->
+      <maxHistory>10</maxHistory>
+    </rollingPolicy>
+
+    <encoder>
+      <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
+    </encoder>
+    
+  </appender>
+
+  <logger name="org" level="WARN"/>
+  <logger name="LogService" level="WARN"/> <!-- Restlet access logging -->
+  <logger name="net.floodlightcontroller.logging" level="WARN"/>
+
+  <root level="DEBUG">
+    <appender-ref ref="ROLLINGFILE" />
+  </root>
+</configuration>
diff --git a/conf/onos-embedded.properties b/conf/onos-embedded.properties
index 83e5ad3..ed85223 100644
--- a/conf/onos-embedded.properties
+++ b/conf/onos-embedded.properties
@@ -1,12 +1,12 @@
 floodlight.modules = net.floodlightcontroller.storage.memory.MemoryStorageSource,\
 net.floodlightcontroller.core.FloodlightProvider,\
 net.floodlightcontroller.threadpool.ThreadPool,\
+net.onrc.onos.ofcontroller.floodlightlistener.NetworkGraphPublisher, \
 net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl,\
 net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
 net.floodlightcontroller.counter.CounterStore,\
 net.floodlightcontroller.perfmon.PktInProcessingTime,\
 net.floodlightcontroller.ui.web.StaticWebRoutable,\
-net.onrc.onos.ofcontroller.floodlightlistener.NetworkGraphPublisher, \
 net.onrc.onos.registry.controller.ZookeeperRegistry
 net.floodlightcontroller.restserver.RestApiServer.port = 8080
 net.floodlightcontroller.core.FloodlightProvider.openflowport = 6633
diff --git a/conf/onos.properties b/conf/onos.properties
index c1e2d27..283203a 100644
--- a/conf/onos.properties
+++ b/conf/onos.properties
@@ -1,12 +1,12 @@
 floodlight.modules = net.floodlightcontroller.storage.memory.MemoryStorageSource,\
 net.floodlightcontroller.core.FloodlightProvider,\
 net.floodlightcontroller.threadpool.ThreadPool,\
+net.onrc.onos.ofcontroller.floodlightlistener.NetworkGraphPublisher, \
 net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl,\
 net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
 net.floodlightcontroller.counter.CounterStore,\
 net.floodlightcontroller.perfmon.PktInProcessingTime,\
 net.floodlightcontroller.ui.web.StaticWebRoutable,\
-net.onrc.onos.ofcontroller.floodlightlistener.NetworkGraphPublisher, \
 net.onrc.onos.registry.controller.ZookeeperRegistry
 net.floodlightcontroller.restserver.RestApiServer.port = 8080
 net.floodlightcontroller.core.FloodlightProvider.openflowport = 6633
diff --git a/lib/jamm-0.2.5.jar b/lib/jamm-0.2.5.jar
new file mode 100644
index 0000000..e9baf75
--- /dev/null
+++ b/lib/jamm-0.2.5.jar
Binary files differ
diff --git a/pom.xml b/pom.xml
index 52fe30a..943133d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,6 +34,7 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <powermock.version>1.5</powermock.version>
     <restlet.version>2.1-RC1</restlet.version>
+    <github.global.server>github</github.global.server>
   </properties>
   <build>
     <plugins>
@@ -121,6 +122,49 @@
           <locale>en</locale>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <version>0.6.3.201306030806</version>
+        <configuration>
+          <destfile>${basedir}/target/jacoco/jacoco.exec</destfile>
+          <datafile>${basedir}/target/jacoco/jacoco.exec</datafile>
+        </configuration>
+        <executions>
+          <execution>
+            <id>jacoco-initialize</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>jacoco-site</id>
+            <phase>package</phase>
+            <goals>
+              <goal>report</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>com.github.github</groupId>
+        <artifactId>site-maven-plugin</artifactId>
+        <version>0.8</version>
+        <configuration>
+          <message>Creating site for ${project.version}</message>
+          <dryRun>true</dryRun>
+          <repositoryName>ONOS</repositoryName>
+          <repositoryOwner>OPENNETWORKINGLAB</repositoryOwner>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+             <goal>site</goal>
+            </goals>
+            <phase>site</phase>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
   <!-- for getting visualization reporting   -->
@@ -128,11 +172,13 @@
     <excludeDefaults>true</excludeDefaults>
     <outputDirectory>${project.build.directory}/site</outputDirectory>
     <plugins>
+      <!--
       <plugin>
         <groupId>org.apache.camel</groupId>
         <artifactId>guice-maven-plugin</artifactId>
         <version>2.11.0</version>
       </plugin>
+      -->
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-project-info-reports-plugin</artifactId>
@@ -145,6 +191,7 @@
           <reportSet>
             <reports>
               <report>dependencies</report>
+              <!--report>maven-emma-plugin</report-->
               <report>scm</report>
             </reports>
           </reportSet>
@@ -211,12 +258,12 @@
     <dependency>
       <groupId>org.codehaus.jackson</groupId>
       <artifactId>jackson-core-asl</artifactId>
-      <version>1.9.11</version>
+      <version>1.9.13</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.jackson</groupId>
       <artifactId>jackson-mapper-asl</artifactId>
-      <version>1.9.11</version>
+      <version>1.9.13</version>
     </dependency>
     <dependency>
       <groupId>ch.qos.logback</groupId>
@@ -258,9 +305,9 @@
       <version>4.1.21</version>
     </dependency>
     <dependency>
-      <groupId>org.jboss.netty</groupId>
-      <artifactId>netty</artifactId>
-      <version>3.2.6.Final</version>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-all</artifactId>
+      <version>4.0.8.Final</version>
     </dependency>
     <dependency>
       <groupId>args4j</groupId>
diff --git a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
index 1e3ec6f..22ff029 100644
--- a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
+++ b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
@@ -206,5 +206,11 @@
     * switch over to ACTIVE role
     */
    public void setAlwaysClearFlowsOnSwAdd(boolean value);
+   
+   /**
+    * Publish updates to Controller updates queue
+    * @param IUpdate
+    */
+   public void publishUpdate(IUpdate update);
 
 }
diff --git a/src/main/java/net/floodlightcontroller/core/IUpdate.java b/src/main/java/net/floodlightcontroller/core/IUpdate.java
new file mode 100644
index 0000000..950bc0f
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/core/IUpdate.java
@@ -0,0 +1,10 @@
+package net.floodlightcontroller.core;
+
+public interface IUpdate {
+	
+    /** 
+     * Calls the appropriate listeners
+     */
+    public void dispatch();
+
+}
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index ac29983..21eceb3 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -58,6 +58,7 @@
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.IOFSwitchFilter;
 import net.floodlightcontroller.core.IOFSwitchListener;
+import net.floodlightcontroller.core.IUpdate;
 import net.floodlightcontroller.core.annotations.LogMessageDoc;
 import net.floodlightcontroller.core.annotations.LogMessageDocs;
 import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState;
@@ -253,17 +254,8 @@
     // Perf. related configuration
     protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
     protected static final int BATCH_MAX_SIZE = 100;
-    protected static final boolean ALWAYS_DECODE_ETH = true;
-
-    /**
-     *  Updates handled by the main loop 
-     */
-    protected interface IUpdate {
-        /** 
-         * Calls the appropriate listeners
-         */
-        public void dispatch();
-    }
+	protected static final boolean ALWAYS_DECODE_ETH = true;
+  
     public enum SwitchUpdateType {
         ADDED,
         REMOVED,
@@ -271,6 +263,7 @@
         PORTADDED,
         PORTREMOVED
     }
+    
     /**
      * Update message indicating a switch was added or removed 
      * ONOS: This message extended to indicate Port add or removed event.
@@ -461,7 +454,13 @@
         }
     }
     
-    
+    public void publishUpdate(IUpdate update) {
+    	try {
+			this.updates.put(update);
+		} catch (InterruptedException e) {
+			log.error("Failure adding update to queue", e);
+		}
+    }
     
     // **********************
     // ChannelUpstreamHandler
diff --git a/src/main/java/net/floodlightcontroller/packet/IPv4.java b/src/main/java/net/floodlightcontroller/packet/IPv4.java
index 01f886d..85f21ca 100644
--- a/src/main/java/net/floodlightcontroller/packet/IPv4.java
+++ b/src/main/java/net/floodlightcontroller/packet/IPv4.java
@@ -31,6 +31,7 @@
  *
  */
 public class IPv4 extends BasePacket {
+	public static final int ADDRESS_LENGTH = 4;
     public static final byte PROTOCOL_ICMP = 0x1;
     public static final byte PROTOCOL_TCP = 0x6;
     public static final byte PROTOCOL_UDP = 0x11;
diff --git a/src/main/java/net/onrc/onos/flow/FlowManagerImpl.java b/src/main/java/net/onrc/onos/flow/FlowManagerImpl.java
index 1f041e6..2e2706c 100644
--- a/src/main/java/net/onrc/onos/flow/FlowManagerImpl.java
+++ b/src/main/java/net/onrc/onos/flow/FlowManagerImpl.java
@@ -28,6 +28,7 @@
 import net.onrc.onos.ofcontroller.util.Dpid;
 import net.onrc.onos.ofcontroller.util.FlowEntry;
 import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+import net.onrc.onos.ofcontroller.util.FlowEntryActions;
 import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
 import net.onrc.onos.ofcontroller.util.FlowPath;
 import net.onrc.onos.ofcontroller.util.Port;
@@ -132,16 +133,12 @@
 		    flowEntry.setOutPort(new Port(src_port.getNumber()));
 		    flowEntry.setFlowEntryMatch(new FlowEntryMatch());
 		    flowEntry.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);
-		    }
+		    FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
 		    FlowEntryAction flowEntryAction = new FlowEntryAction();
 		    flowEntryAction.setActionOutput(flowEntry.outPort());
-		    flowEntryActions.add(flowEntryAction);
+		    flowEntryActions.addAction(flowEntryAction);
 		    dataPath.flowEntries().add(flowEntry);
 		    
 		    FlowPath flowPath = new FlowPath();
@@ -254,14 +251,10 @@
 		    flowEntry.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);
-		    }
+		    FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
 		    FlowEntryAction flowEntryAction = new FlowEntryAction();
 		    flowEntryAction.setActionOutput(flowEntry.outPort());
-		    flowEntryActions.add(flowEntryAction);
+		    flowEntryActions.addAction(flowEntryAction);
 		    dataPath.flowEntries().add(flowEntry);
 			continue;
 		    }
@@ -276,14 +269,10 @@
 		    flowEntry.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);
-		    }
+		    FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
 		    FlowEntryAction flowEntryAction = new FlowEntryAction();
 		    flowEntryAction.setActionOutput(flowEntry.outPort());
-		    flowEntryActions.add(flowEntryAction);
+		    flowEntryActions.addAction(flowEntryAction);
 		    dataPath.flowEntries().add(flowEntry);
 		    dataPath.flowEntries().add(flowEntry);
 		}
diff --git a/src/main/java/net/onrc/onos/graph/GraphDBOperation.java b/src/main/java/net/onrc/onos/graph/GraphDBOperation.java
index acfe43b..f1e9b46 100644
--- a/src/main/java/net/onrc/onos/graph/GraphDBOperation.java
+++ b/src/main/java/net/onrc/onos/graph/GraphDBOperation.java
@@ -3,8 +3,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.openflow.protocol.OFPhysicalPort;
-
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowPath;
@@ -14,7 +12,6 @@
 import net.onrc.onos.ofcontroller.util.FlowEntryId;
 import net.onrc.onos.ofcontroller.util.FlowId;
 
-import com.google.common.base.Stopwatch;
 import com.thinkaurelius.titan.core.TitanGraph;
 import com.tinkerpop.blueprints.Vertex;
 import com.tinkerpop.frames.FramedGraph;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java
index 7425a07..fa11c17 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java
@@ -2,41 +2,25 @@
 
 import java.net.InetAddress;
 
-import net.floodlightcontroller.util.MACAddress;
-
 import org.codehaus.jackson.annotate.JsonProperty;
 
 import com.google.common.net.InetAddresses;
 
 public class BgpPeer {
-	private String interfaceName;
-	private InetAddress ipAddress;
-	private MACAddress macAddress;
+	private final String interfaceName;
+	private final InetAddress ipAddress;
+	
+	public BgpPeer(@JsonProperty("interface") String interfaceName,
+				   @JsonProperty("ipAddress") String ipAddress) {
+		this.interfaceName = interfaceName;
+		this.ipAddress = InetAddresses.forString(ipAddress);
+	}
 	
 	public String getInterfaceName() {
 		return interfaceName;
 	}
-	
-	@JsonProperty("interface")
-	public void setInterfaceName(String interfaceName) {
-		this.interfaceName = interfaceName;
-	}
-	
+
 	public InetAddress getIpAddress() {
 		return ipAddress;
 	}
-	
-	@JsonProperty("ipAddress")
-	public void setIpAddress(String ipAddress) {
-		this.ipAddress = InetAddresses.forString(ipAddress);
-	}
-	
-	public MACAddress getMacAddress() {
-		return macAddress;
-	}
-	
-	@JsonProperty("macAddress")
-	public void setMacAddress(String macAddress) {
-		this.macAddress = MACAddress.valueOf(macAddress);
-	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
index 2034118..826fd93 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
@@ -3,14 +3,17 @@
 import java.io.File;
 import java.io.IOException;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
@@ -22,18 +25,22 @@
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.core.util.SingletonTask;
-import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.routing.Link;
 import net.floodlightcontroller.topology.ITopologyListener;
 import net.floodlightcontroller.topology.ITopologyService;
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.bgproute.RibUpdate.Operation;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyService.ITopoLinkService;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyService.ITopoRouteService;
 import net.onrc.onos.ofcontroller.core.internal.TopoLinkServiceImpl;
 import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery;
 import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
+import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryService;
+import net.onrc.onos.ofcontroller.proxyarp.IArpRequester;
+import net.onrc.onos.ofcontroller.proxyarp.IProxyArpService;
 import net.onrc.onos.ofcontroller.proxyarp.ProxyArpManager;
 import net.onrc.onos.ofcontroller.routing.TopoRouteService;
 import net.onrc.onos.ofcontroller.util.DataPath;
@@ -50,7 +57,6 @@
 import org.codehaus.jackson.map.ObjectMapper;
 import org.openflow.protocol.OFFlowMod;
 import org.openflow.protocol.OFMatch;
-import org.openflow.protocol.OFMessage;
 import org.openflow.protocol.OFPacketOut;
 import org.openflow.protocol.OFPort;
 import org.openflow.protocol.OFType;
@@ -61,20 +67,32 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.net.InetAddresses;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
 public class BgpRoute implements IFloodlightModule, IBgpRouteService, 
-									ITopologyListener, IOFSwitchListener {
+									ITopologyListener, IArpRequester,
+									IOFSwitchListener, ILayer3InfoService,
+									IProxyArpService {
 	
 	protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
 
 	protected IFloodlightProviderService floodlightProvider;
 	protected ITopologyService topology;
 	protected ITopoRouteService topoRouteService;
-	protected IDeviceService devices;
+	protected ILinkDiscoveryService linkDiscoveryService;
 	protected IRestApiService restApi;
 	
 	protected ProxyArpManager proxyArp;
 	
-	protected static Ptree ptree;
+	protected IPatriciaTrie<RibEntry> ptree;
+	protected IPatriciaTrie<Interface> interfacePtrie;
+	protected BlockingQueue<RibUpdate> ribUpdates;
+	
 	protected String bgpdRestIp;
 	protected String routerId;
 	protected String configFilename = "config.json";
@@ -91,6 +109,7 @@
 	//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;
+	protected final short ARP_PRIORITY = 20;
 	
 	protected final short BGP_PORT = 179;
 	
@@ -101,6 +120,7 @@
 	protected Map<String, Interface> interfaces;
 	protected Map<InetAddress, BgpPeer> bgpPeers;
 	protected SwitchPort bgpdAttachmentPoint;
+	protected MACAddress bgpdMacAddress;
 	
 	//True when all switches have connected
 	protected volatile boolean switchesConnected = false;
@@ -110,6 +130,20 @@
 	protected ArrayList<LDUpdate> linkUpdates;
 	protected SingletonTask topologyChangeDetectorTask;
 	
+	protected SetMultimap<InetAddress, RibUpdate> prefixesWaitingOnArp;
+	
+	protected Map<InetAddress, Path> pathsWaitingOnArp;
+	
+	protected ExecutorService bgpUpdatesExecutor;
+	
+	protected Map<InetAddress, Path> pushedPaths;
+	protected Map<Prefix, Path> prefixToPath;
+	protected Multimap<Prefix, PushedFlowMod> pushedFlows;
+	
+	private FlowCache flowCache;
+	
+	protected volatile Map<Long, ?> topoRouteTopology = null;
+		
 	protected class TopologyChangeDetector implements Runnable {
 		@Override
 		public void run() {
@@ -119,9 +153,6 @@
 				ITopoLinkService topoLinkService = new TopoLinkServiceImpl();
 				
 				List<Link> activeLinks = topoLinkService.getActiveLinks();
-				for (Link l : activeLinks){
-					log.debug("active link: {}", l);
-				}
 				
 				Iterator<LDUpdate> it = linkUpdates.iterator();
 				while (it.hasNext()){
@@ -130,22 +161,23 @@
 							ldu.getDst(), ldu.getDstPort());
 					
 					if (activeLinks.contains(l)){
-						log.debug("Not found: {}", l);
 						it.remove();
 					}
 				}
 			}
 			
-			if (linkUpdates.isEmpty()){
-				//All updates have been seen in network map.
-				//We can check if topology is ready
-				log.debug("No know changes outstanding. Checking topology now");
-				checkStatus();
-			}
-			else {
-				//We know of some link updates that haven't propagated to the database yet
-				log.debug("Some changes not found in network map- size {}", linkUpdates.size());
-				topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
+			if (!topologyReady) {
+				if (linkUpdates.isEmpty()){
+					//All updates have been seen in network map.
+					//We can check if topology is ready
+					log.debug("No known changes outstanding. Checking topology now");
+					checkStatus();
+				}
+				else {
+					//We know of some link updates that haven't propagated to the database yet
+					log.debug("Some changes not found in network map - {} links missing", linkUpdates.size());
+					topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
+				}
 			}
 		}
 	}
@@ -171,6 +203,7 @@
 					new Dpid(config.getBgpdAttachmentDpid()),
 					new Port(config.getBgpdAttachmentPort()));
 			
+			bgpdMacAddress = config.getBgpdMacAddress();
 		} catch (JsonParseException e) {
 			log.error("Error in JSON file", e);
 			System.exit(1);
@@ -181,6 +214,12 @@
 			log.error("Error reading JSON file", e);
 			System.exit(1);
 		}
+		
+		//Populate the interface Patricia Trie
+		for (Interface intf : interfaces.values()) {
+			Prefix prefix = new Prefix(intf.getIpAddress().getAddress(), intf.getPrefixLength());
+			interfacePtrie.put(prefix, intf);
+		}
 	}
 	
 	@Override
@@ -196,6 +235,7 @@
 		Map<Class<? extends IFloodlightService>, IFloodlightService> m 
 			= new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
 		m.put(IBgpRouteService.class, this);
+		m.put(IProxyArpService.class, this);
 		return m;
 	}
 
@@ -205,7 +245,6 @@
 			= new ArrayList<Class<? extends IFloodlightService>>();
 		l.add(IFloodlightProviderService.class);
 		l.add(ITopologyService.class);
-		l.add(IDeviceService.class);
 		l.add(IRestApiService.class);
 		return l;
 	}
@@ -214,17 +253,20 @@
 	public void init(FloodlightModuleContext context)
 			throws FloodlightModuleException {
 	    
-	    ptree = new Ptree(32);
+		ptree = new PatriciaTrie<RibEntry>(32);
+		interfacePtrie = new PatriciaTrie<Interface>(32);
+	    
+	    ribUpdates = new LinkedBlockingQueue<RibUpdate>();
 	    	
 		// Register floodlight provider and REST handler.
 		floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
 		topology = context.getServiceImpl(ITopologyService.class);
-		devices = context.getServiceImpl(IDeviceService.class);
+		linkDiscoveryService = context.getServiceImpl(ILinkDiscoveryService.class);
 		restApi = context.getServiceImpl(IRestApiService.class);
 		
 		//TODO We'll initialise this here for now, but it should really be done as
 		//part of the controller core
-		proxyArp = new ProxyArpManager(floodlightProvider, topology);
+		proxyArp = new ProxyArpManager(floodlightProvider, topology, this, restApi);
 		
 		linkUpdates = new ArrayList<LDUpdate>();
 		ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
@@ -232,6 +274,19 @@
 
 		topoRouteService = new TopoRouteService("");
 		
+		pathsWaitingOnArp = new HashMap<InetAddress, Path>();
+		prefixesWaitingOnArp = Multimaps.synchronizedSetMultimap(
+				HashMultimap.<InetAddress, RibUpdate>create());
+		
+		pushedPaths = new HashMap<InetAddress, Path>();
+		prefixToPath = new HashMap<Prefix, Path>();
+		pushedFlows = HashMultimap.<Prefix, PushedFlowMod>create();
+		
+		flowCache = new FlowCache(floodlightProvider);
+		
+		bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
+				new ThreadFactoryBuilder().setNameFormat("bgp-updates-%d").build());
+		
 		//Read in config values
 		bgpdRestIp = context.getConfigParams(this).get("BgpdRestIp");
 		if (bgpdRestIp == null){
@@ -258,17 +313,28 @@
 		log.debug("Config file set to {}", configFilename);
 		
 		readGatewaysConfiguration(configFilename);
-		// Test.
-		//test();
+	}
+	
+	@Override
+	public void startUp(FloodlightModuleContext context) {
+		restApi.addRestletRoutable(new BgpRouteWebRoutable());
+		topology.addListener(this);
+		floodlightProvider.addOFSwitchListener(this);
+		
+		proxyArp.startUp();
+		
+		floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
+		
+		//Retrieve the RIB from BGPd during startup
+		retrieveRib();
 	}
 
-	public Ptree getPtree() {
+	public IPatriciaTrie<RibEntry> getPtree() {
 		return ptree;
 	}
 	
 	public void clearPtree() {
-		//ptree = null;
-		ptree = new Ptree(32);	
+		ptree = new PatriciaTrie<RibEntry>(32);
 	}
 	
 	public String getBGPdRestIp() {
@@ -279,98 +345,6 @@
 		return routerId;
 	}
 	
-	// Return nexthop address as byte array.
-	public Rib lookupRib(byte[] dest) {
-		if (ptree == null) {
-		    log.debug("lookupRib: ptree null");
-		    return null;
-		}
-		
-		PtreeNode node = ptree.match(dest, 32);
-		if (node == null) {
-            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() 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);
-		Prefix r = new Prefix("10.0.0.0", 24);
-		Prefix a = new Prefix("10.0.0.1", 32);
-	
-		ptree.acquire(p.getAddress(), p.masklen);
-		ptree.acquire(q.getAddress(), q.masklen);
-		ptree.acquire(r.getAddress(), r.masklen);
-	
-		System.out.println("Traverse start");
-		for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
-			Prefix p_result = new Prefix(node.key, node.keyBits);
-		}
-	
-		PtreeNode n = ptree.match(a.getAddress(), a.masklen);
-		if (n != null) {
-			System.out.println("Matched prefix for 10.0.0.1:");
-			Prefix x = new Prefix(n.key, n.keyBits);
-			ptree.delReference(n);
-		}
-		
-		n = ptree.lookup(p.getAddress(), p.masklen);
-		if (n != null) {
-			ptree.delReference(n);
-			ptree.delReference(n);
-		}
-		System.out.println("Traverse start");
-		for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
-			Prefix p_result = new Prefix(node.key, node.keyBits);
-		}
-		
-		n = ptree.lookup(q.getAddress(), q.masklen);
-		if (n != null) {
-			ptree.delReference(n);
-			ptree.delReference(n);
-		}
-		System.out.println("Traverse start");
-		for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
-			Prefix p_result = new Prefix(node.key, node.keyBits);
-		}
-		
-		n = ptree.lookup(r.getAddress(), r.masklen);
-		if (n != null) {
-			ptree.delReference(n);
-			ptree.delReference(n);
-		}
-		System.out.println("Traverse start");
-		for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
-			Prefix p_result = new Prefix(node.key, node.keyBits);
-		}
-
-	}
-	
-	private String getPrefixFromPtree(PtreeNode node){
-        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 "";
-		}
-        return address.toString() + "/" + node.rib.masklen;
-	}
-	
 	private void retrieveRib(){
 		String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
 		String response = RestClient.get(url);
@@ -404,81 +378,147 @@
 			} catch (NumberFormatException e) {
 				log.warn("Wrong mask format in RIB JSON: {}", mask1);
 				continue;
-			} catch (UnknownHostException e1) {
+			} catch (IllegalArgumentException 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);
+			RibEntry rib = new RibEntry(router_id, nexthop);
+
+			try {
+				ribUpdates.put(new RibUpdate(Operation.UPDATE, p, rib));
+			} catch (InterruptedException e) {
+				log.debug("Interrupted while pushing onto update queue");
 			}
-			
-			node.rib = rib;
-			
-			prefixAdded(node);
 		} 
 	}
 	
-	public void prefixAdded(PtreeNode node) {
-		if (!topologyReady){
+	@Override
+	public void newRibUpdate(RibUpdate update) {
+		try {
+			ribUpdates.put(update);
+		} catch (InterruptedException e) {
+			log.debug("Interrupted while putting on ribUpdates queue", e);
+			Thread.currentThread().interrupt();
+		}
+	}
+	
+	public synchronized void processRibAdd(RibUpdate update) {
+		Prefix prefix = update.getPrefix();
+		
+		log.debug("Processing prefix add {}", prefix);
+		
+		RibEntry rib = ptree.put(prefix, update.getRibEntry());
+		
+		if (rib != null && !rib.equals(update.getRibEntry())) {
+			//There was an existing nexthop for this prefix. This update supersedes that,
+			//so we need to remove the old flows for this prefix from the switches
+			_processDeletePrefix(prefix, rib);
+		}
+		
+		if (update.getRibEntry().getNextHop().equals(
+				InetAddresses.forString("0.0.0.0"))) {
+			//Route originated by SDN domain
+			//We don't handle these at the moment
+			log.debug("Own route {} to {}", prefix, 
+					update.getRibEntry().getNextHop().getHostAddress());
 			return;
 		}
 		
-		String prefix = getPrefixFromPtree(node);
+		_processRibAdd(update);
+	}
+	
+	private void _processRibAdd(RibUpdate update) {
+		Prefix prefix = update.getPrefix();
+		RibEntry rib = update.getRibEntry();
 		
-		log.debug("New prefix {} added, next hop {}, routerId {}", 
-				new Object[] {prefix, node.rib.nextHop.toString(), 
-				node.rib.routerId.getHostAddress()});
+		InetAddress dstIpAddress = rib.getNextHop();
 		
-		//TODO this is wrong, we shouldn't be dealing with BGP peers here.
-		//We need to figure out where the device is attached and what it's
-		//mac address is by learning. 
-		//The next hop is not necessarily the peer, and the peer's attachment
-		//point is not necessarily the next hop's attachment point.
-		BgpPeer peer = bgpPeers.get(node.rib.nextHop);
+		//See if we know the MAC address of the next hop
+		MACAddress nextHopMacAddress = proxyArp.getMacAddress(rib.getNextHop());
 		
-		if (peer == null){
-			//TODO local router isn't in peers list so this will get thrown
-			//Need to work out what to do about local prefixes with next hop 0.0.0.0.
-			
-			//The other scenario is this is a route server route. In that
-			//case the next hop is not in our configuration
-			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
+		//Find the attachment point (egress interface) of the next hop
+		Interface egressInterface = null;
+		if (bgpPeers.containsKey(dstIpAddress)) {
+			//Route to a peer
+			log.debug("Route to peer {}", dstIpAddress);
+			BgpPeer peer = bgpPeers.get(dstIpAddress);
+			egressInterface = interfaces.get(peer.getInterfaceName());
+		}
+		else {
+			//Route to non-peer
+			log.debug("Route to non-peer {}", dstIpAddress);
+			egressInterface = interfacePtrie.match(
+					new Prefix(dstIpAddress.getAddress(), 32));
+			if (egressInterface == null) {
+				log.warn("No outgoing interface found for {}", dstIpAddress.getHostAddress());
+				return;
+			}
 		}
 		
-		Interface peerInterface = interfaces.get(peer.getInterfaceName());
-
-		//Add a flow to rewrite mac for this prefix to all border switches
-		for (Interface srcInterface : interfaces.values()) {
-			if (srcInterface == peerInterface) {
-				//Don't push a flow for the switch where this peer is attached
-				continue;
+		if (nextHopMacAddress == null) {
+			prefixesWaitingOnArp.put(dstIpAddress, 
+					new RibUpdate(Operation.UPDATE, prefix, rib));
+			proxyArp.sendArpRequest(dstIpAddress, this, true);
+			return;
+		}
+		else {
+			if (!bgpPeers.containsKey(dstIpAddress)) {
+				//If the prefix is for a non-peer we need to ensure there's a path,
+				//and push one if there isn't.
+				Path path = pushedPaths.get(dstIpAddress);
+				if (path == null) {
+					path = new Path(egressInterface, dstIpAddress);
+					calculateAndPushPath(path, nextHopMacAddress);
+					pushedPaths.put(dstIpAddress, path);
+				}
+				
+				path.incrementUsers();
+				prefixToPath.put(prefix, path);
 			}
-						
-			DataPath shortestPath = topoRouteService.getShortestPath(
-					srcInterface.getSwitchPort(),
-					peerInterface.getSwitchPort());
+			
+			//For all prefixes we need to add the first-hop mac-rewriting flows
+			addPrefixFlows(prefix, egressInterface, nextHopMacAddress);
+		}
+	}
+	
+	private void addPrefixFlows(Prefix prefix, Interface egressInterface, MACAddress nextHopMacAddress) {		
+		log.debug("Adding flows for prefix {} added, next hop mac {}",
+				prefix, nextHopMacAddress);
+		
+		//We only need one flow mod per switch, so pick one interface on each switch
+		Map<Long, Interface> srcInterfaces = new HashMap<Long, Interface>();
+		for (Interface intf : interfaces.values()) {
+			if (!srcInterfaces.containsKey(intf.getDpid()) 
+					&& intf != egressInterface) {
+				srcInterfaces.put(intf.getDpid(), intf);
+			}
+		}
+		
+		//Add a flow to rewrite mac for this prefix to all other border switches
+		for (Interface srcInterface : srcInterfaces.values()) {
+			DataPath shortestPath; 
+			if (topoRouteTopology == null) {
+				shortestPath = topoRouteService.getShortestPath(
+						srcInterface.getSwitchPort(),
+						egressInterface.getSwitchPort());
+			}
+			else {
+				shortestPath = topoRouteService.getTopoShortestPath(
+						topoRouteTopology, srcInterface.getSwitchPort(),
+						egressInterface.getSwitchPort());
+			}
 			
 			if (shortestPath == null){
 				log.debug("Shortest path between {} and {} not found",
 						srcInterface.getSwitchPort(),
-						peerInterface.getSwitchPort());
+						egressInterface.getSwitchPort());
 				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);
+			OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
+					.getMessage(OFType.FLOW_MOD);
 			
 	        fm.setIdleTimeout((short)0)
 	        .setHardTimeout((short)0)
@@ -494,32 +534,16 @@
 	        match.setDataLayerType(Ethernet.TYPE_IPv4);
 	        match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
 	        
-	        //match.setDataLayerSource(ingressRouter.getRouterMac().toBytes());
-	        //match.setDataLayerSource(peer.getMacAddress().toBytes());
-	        //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
-
-	        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);
+	        match.setFromCIDR(prefix.toString(), OFMatch.STR_NW_DST);
 	        fm.setMatch(match);
 	        
 	        //Set up MAC rewrite action
 	        OFActionDataLayerDestination macRewriteAction = new OFActionDataLayerDestination();
-	        //TODO use ARP module rather than configured mac addresses
-	        //TODO the peer's mac address is not necessarily the next hop's...
-	        macRewriteAction.setDataLayerAddress(peer.getMacAddress().toBytes());
+	        macRewriteAction.setDataLayerAddress(nextHopMacAddress.toBytes());
 	        
 	        //Set up output action
 	        OFActionOutput outputAction = new OFActionOutput();
 	        outputAction.setMaxLength((short)0xffff);
-	        
 	        Port outputPort = shortestPath.flowEntries().get(0).outPort();
 	        outputAction.setPort(outputPort.value());
 	        
@@ -528,107 +552,101 @@
 	        actions.add(outputAction);
 	        fm.setActions(actions);
 	        
-	        //Write to switch
-	        IOFSwitch sw = floodlightProvider.getSwitches()
-	        			.get(srcInterface.getDpid());
-	        
-            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);
+	        pushedFlows.put(prefix, new PushedFlowMod(srcInterface.getDpid(), fm));
+	        flowCache.write(srcInterface.getDpid(), fm);
+
+			/*
+			 * XXX Rate limit hack!
+			 * This should be solved properly by adding a rate limiting
+			 * layer on top of the switches if we know they need it.
+			 */
+	        try {
+				Thread.sleep(1);
+			} catch (InterruptedException e) {
+				// TODO handle this properly
+				log.error("Interrupted", e);
 			}
 		}
 	}
 	
-	//TODO this is largely untested
-	public void prefixDeleted(PtreeNode node) {
-		if (!topologyReady) {
-			return;
+	public synchronized void processRibDelete(RibUpdate update) {
+		Prefix prefix = update.getPrefix();
+		
+		if (ptree.remove(prefix, update.getRibEntry())) {
+			/*
+			 * Only delete flows if an entry was actually removed from the trie.
+			 * If no entry was removed, the <prefix, nexthop> wasn't there so
+			 * it's probably already been removed and we don't need to do anything
+			 */
+			_processDeletePrefix(prefix, update.getRibEntry());
 		}
+	}
+	
+	private void _processDeletePrefix(Prefix prefix, RibEntry ribEntry) {
+		deletePrefixFlows(prefix);
 		
-		String prefix = getPrefixFromPtree(node);
+		log.debug("Deleting {} to {}", prefix, ribEntry.getNextHop());
 		
-		log.debug("Prefix {} deleted, next hop {}", 
-				prefix, node.rib.nextHop.toString());
-		
-		//Remove MAC rewriting flows from other border switches
-		BgpPeer peer = bgpPeers.get(node.rib.nextHop);
-		if (peer == null){
-			//either a router server route or local route. Can't handle right now
-			return;
-		}
-		
-		Interface peerInterface = interfaces.get(peer.getInterfaceName());
-		
-		for (Interface srcInterface : interfaces.values()) {
-			if (srcInterface == peerInterface) {
-				continue;
-			}
+		if (!bgpPeers.containsKey(ribEntry.getNextHop())) {
+			log.debug("Getting path for route with non-peer nexthop");
+			Path path = prefixToPath.remove(prefix);
 			
-			//Set up the flow mod
-			OFFlowMod fm =
-	                (OFFlowMod) floodlightProvider.getOFMessageFactory()
-	                                              .getMessage(OFType.FLOW_MOD);
+			if (path != null) {
+				//path could be null if we added to the Ptree but didn't push
+				//flows yet because we were waiting to resolve ARP
 			
-	        fm.setIdleTimeout((short)0)
-	        .setHardTimeout((short)0)
-	        .setBufferId(OFPacketOut.BUFFER_ID_NONE)
-	        .setCookie(MAC_RW_COOKIE)
-	        .setCommand(OFFlowMod.OFPFC_DELETE)
-	        .setOutPort(OFPort.OFPP_NONE)
-	        .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.setDataLayerSource(peer.getMacAddress().toBytes());
-	        //match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
-	        
-	        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);
-	        
-	        //Write to switch
-	        IOFSwitch sw = floodlightProvider.getSwitches()
-	        		.get(srcInterface.getDpid());
-	        
-            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);
+				path.decrementUsers();
+				if (path.getUsers() <= 0 && !path.isPermanent()) {
+					deletePath(path);
+					pushedPaths.remove(path.getDstIpAddress());
+				}
 			}
 		}
 	}
 	
+	private void deletePrefixFlows(Prefix prefix) {
+		log.debug("Deleting flows for prefix {}", prefix);
+		
+		Collection<PushedFlowMod> pushedFlowMods 
+				= pushedFlows.removeAll(prefix);
+		
+		for (PushedFlowMod pfm : pushedFlowMods) {
+			if (log.isTraceEnabled()) {
+				log.trace("Pushing a DELETE flow mod to {}, matches prefix {} with mac-rewrite {}",
+						new Object[] {HexString.toHexString(pfm.getDpid()),
+						pfm.getFlowMod().getMatch().getNetworkDestination() + 
+						pfm.getFlowMod().getMatch().getNetworkDestinationMaskLen(),
+						HexString.toHexString(((OFActionDataLayerDestination)pfm.getFlowMod().getActions().get(0))
+								.getDataLayerAddress())});
+			}
+			
+			sendDeleteFlowMod(pfm.getFlowMod(), pfm.getDpid());
+		}
+	}
+	
+	private void deletePath(Path path) {
+		log.debug("Deleting flows for path to {}", 
+				path.getDstIpAddress().getHostAddress());
+		
+		for (PushedFlowMod pfm : path.getFlowMods()) {
+			if (log.isTraceEnabled()) {
+				log.trace("Pushing a DELETE flow mod to {}, dst MAC {}",
+						new Object[] {HexString.toHexString(pfm.getDpid()),
+						HexString.toHexString(pfm.getFlowMod().getMatch().getDataLayerDestination())
+				});
+			}
+			
+			sendDeleteFlowMod(pfm.getFlowMod(), pfm.getDpid());
+		}
+	}
+	
+	private void sendDeleteFlowMod(OFFlowMod addFlowMod, long dpid) {
+		flowCache.delete(dpid, addFlowMod);
+	}
+	
+	//TODO test next-hop changes
+	//TODO check delete/add synchronization
+	
 	/*
 	 * On startup we need to calculate a full mesh of paths between all gateway
 	 * switches
@@ -642,30 +660,68 @@
 		
 		for (BgpPeer peer : bgpPeers.values()) {
 			Interface peerInterface = interfaces.get(peer.getInterfaceName());
-			//for (Map.Entry<String, Interface> intfEntry : interfaces.entrySet()) {
-			for (Interface srcInterface : interfaces.values()) {
-				//Interface srcInterface = intfEntry.getValue();
-				//if (peer.getInterfaceName().equals(intfEntry.getKey())){
-				if (peer.getInterfaceName().equals(srcInterface.getName())){
-					continue;
-				}
+			
+			//We know there's not already a Path here pushed, because this is
+			//called before all other routing
+			Path path = new Path(peerInterface, peer.getIpAddress());
+			path.setPermanent();
+			
+			//See if we know the MAC address of the peer. If not we can't
+			//do anything until we learn it
+			MACAddress macAddress = proxyArp.getMacAddress(peer.getIpAddress());
+			if (macAddress == null) {
+				log.debug("Don't know MAC for {}", peer.getIpAddress().getHostAddress());
+				//Put in the pending paths list first
+				pathsWaitingOnArp.put(peer.getIpAddress(), path);
 				
-				DataPath shortestPath = topoRouteService.getShortestPath(
-							srcInterface.getSwitchPort(), peerInterface.getSwitchPort()); 
-				
-				if (shortestPath == null){
-					log.debug("Shortest path between {} and {} not found",
-							srcInterface.getSwitchPort(), peerInterface.getSwitchPort());
-					return; // just quit here?
-				}
-				
-				//install flows
-				installPath(shortestPath.flowEntries(), peer);
+				proxyArp.sendArpRequest(peer.getIpAddress(), this, true);
+				continue;
 			}
+			
+			//If we know the MAC, lets go ahead and push the paths to this peer
+			calculateAndPushPath(path, macAddress);
 		}
 	}
 	
-	private void installPath(List<FlowEntry> flowEntries, BgpPeer peer){
+	private void calculateAndPushPath(Path path, MACAddress dstMacAddress) {
+		Interface dstInterface = path.getDstInterface();
+		
+		log.debug("Setting up path to {}, {}", path.getDstIpAddress().getHostAddress(),
+				dstMacAddress);
+		
+		List<PushedFlowMod> pushedFlows = new ArrayList<PushedFlowMod>();
+		
+		for (Interface srcInterface : interfaces.values()) {
+			if (dstInterface.equals(srcInterface.getName())){
+				continue;
+			}
+			
+			DataPath shortestPath;
+			if (topoRouteTopology == null) {
+				shortestPath = topoRouteService.getShortestPath(
+						srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
+			}
+			else {
+				shortestPath = topoRouteService.getTopoShortestPath(topoRouteTopology, 
+						srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
+			}
+			
+			if (shortestPath == null){
+				log.warn("Shortest path between {} and {} not found",
+						srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
+				return;
+			}
+			
+			List<PushedFlowMod> pushedFlowMods = installPath(shortestPath.flowEntries(), dstMacAddress);
+			pushedFlows.addAll(pushedFlowMods);
+		}
+		
+		path.setFlowMods(pushedFlows);
+	}
+	
+	private List<PushedFlowMod> installPath(List<FlowEntry> flowEntries, MACAddress dstMacAddress){
+		List<PushedFlowMod> flowMods = new ArrayList<PushedFlowMod>();
+		
 		//Set up the flow mod
 		OFFlowMod fm =
                 (OFFlowMod) floodlightProvider.getOFMessageFactory()
@@ -681,6 +737,7 @@
         .setBufferId(OFPacketOut.BUFFER_ID_NONE)
         .setCookie(L2_FWD_COOKIE)
         .setCommand(OFFlowMod.OFPFC_ADD)
+        .setPriority(SDNIP_PRIORITY)
         .setActions(actions)
         .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
         
@@ -690,35 +747,24 @@
         	FlowEntry flowEntry = flowEntries.get(i);
            
             OFMatch match = new OFMatch();
-            //TODO Again using MAC address from configuration
-            match.setDataLayerDestination(peer.getMacAddress().toBytes());
+            match.setDataLayerDestination(dstMacAddress.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());
+            flowMods.add(new PushedFlowMod(flowEntry.dpid().value(), fm));
             
-            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);
-			}
-            
+            flowCache.write(flowEntry.dpid().value(), fm);
+                        
             try {
                 fm = fm.clone();
             } catch (CloneNotSupportedException e1) {
                 log.error("Failure cloning flow mod", e1);
             }
 		}
+        
+        return flowMods;
 	}
 	
 	private void setupBgpPaths(){
@@ -756,17 +802,15 @@
 	        //Forward = gateway -> bgpd, reverse = bgpd -> gateway
 	        OFMatch forwardMatchSrc = new OFMatch();
 	        
-	        
 	        String interfaceCidrAddress = peerInterface.getIpAddress().getHostAddress() 
 	        					+ "/32";
 	        String peerCidrAddress = bgpPeer.getIpAddress().getHostAddress()
 	        					+ "/32";
-	        	        
+	        
 	        //Common match fields
 	        forwardMatchSrc.setDataLayerType(Ethernet.TYPE_IPv4);
-	        //forwardMatch.setWildcards(forwardMatch.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
 	        forwardMatchSrc.setNetworkProtocol(IPv4.PROTOCOL_TCP);
-	        forwardMatchSrc.setTransportDestination(BGP_PORT);
+	        //forwardMatchSrc.setTransportDestination(BGP_PORT);
 	        forwardMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_IN_PORT
 	        				& ~OFMatch.OFPFW_DL_TYPE & ~OFMatch.OFPFW_NW_PROTO);
 	        
@@ -795,14 +839,27 @@
 	        
 	        fm.setMatch(forwardMatchSrc);
 	        
+	        OFMatch forwardIcmpMatch = new OFMatch();
+	        forwardIcmpMatch.setDataLayerType(Ethernet.TYPE_IPv4);
+	        forwardIcmpMatch.setNetworkProtocol(IPv4.PROTOCOL_ICMP);
+	        forwardIcmpMatch.setWildcards(forwardIcmpMatch.getWildcards() &
+	        		~OFMatch.OFPFW_DL_TYPE & ~OFMatch.OFPFW_NW_PROTO);
+	        
+	        OFMatch reverseIcmpMatch = forwardIcmpMatch.clone();
+	        forwardIcmpMatch.setFromCIDR(interfaceCidrAddress, OFMatch.STR_NW_DST);
+	        reverseIcmpMatch.setFromCIDR(interfaceCidrAddress, OFMatch.STR_NW_SRC);
+	        
 			for (FlowEntry flowEntry : path.flowEntries()){
 				OFFlowMod forwardFlowModSrc, forwardFlowModDst;
 				OFFlowMod reverseFlowModSrc, reverseFlowModDst;
+				OFFlowMod forwardIcmp, reverseIcmp;
 				try {
 					forwardFlowModSrc = fm.clone();
 					forwardFlowModDst = fm.clone();
 					reverseFlowModSrc = fm.clone();
 					reverseFlowModDst = fm.clone();
+					forwardIcmp = fm.clone();
+					reverseIcmp = fm.clone();
 				} catch (CloneNotSupportedException e) {
 					log.warn("Clone failed", e);
 					continue;
@@ -828,36 +885,186 @@
 				((OFActionOutput)reverseFlowModDst.getActions().get(0))
 						.setPort(flowEntry.inPort().value());
 				
-				IOFSwitch sw = floodlightProvider.getSwitches().get(flowEntry.dpid().value());
+				((OFActionOutput)forwardIcmp.getActions().get(0))
+						.setPort(flowEntry.outPort().value());
+				forwardIcmp.setMatch(forwardIcmpMatch);
 				
-				//Hopefully the switch is there
-				List<OFMessage> msgList = new ArrayList<OFMessage>(2);
-				msgList.add(forwardFlowModSrc);
-				msgList.add(forwardFlowModDst);
-				msgList.add(reverseFlowModSrc);
-				msgList.add(reverseFlowModDst);
+				((OFActionOutput)reverseIcmp.getActions().get(0))
+						.setPort(flowEntry.inPort().value());
+				reverseIcmp.setMatch(reverseIcmpMatch);
 				
-				try {
-					sw.write(msgList, null);
-					sw.flush();
-				} catch (IOException e) {
-					log.error("Failure writing flow mod", e);
+				List<OFFlowMod> flowModList = new ArrayList<OFFlowMod>(6);
+				flowModList.add(forwardFlowModSrc);
+				flowModList.add(forwardFlowModDst);
+				flowModList.add(reverseFlowModSrc);
+				flowModList.add(reverseFlowModDst);
+				flowModList.add(forwardIcmp);
+				flowModList.add(reverseIcmp);
+				flowCache.write(flowEntry.dpid().value(), flowModList);
+			}
+		}
+	}
+	
+	@Override
+	public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
+		log.debug("Received ARP response: {} => {}", 
+				ipAddress.getHostAddress(), macAddress);
+		
+		/*
+		 * We synchronize on this to prevent changes to the ptree while we're pushing
+		 * flows to the switches. If the ptree changes, the ptree and switches
+		 * could get out of sync. 
+		 */
+		synchronized (this) {
+			Path path = pathsWaitingOnArp.remove(ipAddress);
+			
+			if (path != null) {
+				log.debug("Pushing path to {} at {} on {}", new Object[] {
+						path.getDstIpAddress().getHostAddress(), macAddress,
+						path.getDstInterface().getSwitchPort()});
+				//These paths should always be to BGP peers. Paths to non-peers are
+				//handled once the first prefix is ready to push
+				if (pushedPaths.containsKey(path.getDstInterface())) {
+					//A path already got pushed to this endpoint while we were waiting
+					//for ARP. We'll copy over the permanent attribute if it is set on this path.
+					if (path.isPermanent()) {
+						pushedPaths.get(path.getDstInterface()).setPermanent();
+					}
+				}
+				else {
+					calculateAndPushPath(path, macAddress);
+					pushedPaths.put(path.getDstIpAddress(), path);
+				}
+			}
+			
+			Set<RibUpdate> prefixesToPush = prefixesWaitingOnArp.removeAll(ipAddress);
+			
+			for (RibUpdate update : prefixesToPush) {
+				//These will always be adds
+				
+				RibEntry rib = ptree.lookup(update.getPrefix()); 
+				if (rib != null && rib.equals(update.getRibEntry())) {
+					log.debug("Pushing prefix {} next hop {}", update.getPrefix(), 
+							rib.getNextHop().getHostAddress());
+					//We only push prefix flows if the prefix is still in the ptree
+					//and the next hop is the same as our update. The prefix could 
+					//have been removed while we were waiting for the ARP, or the 
+					//next hop could have changed.
+					_processRibAdd(update);
+				} else {
+					log.debug("Received ARP response, but {},{} is no longer in ptree", 
+							update.getPrefix(), update.getRibEntry());
 				}
 			}
 		}
 	}
 	
+	private void setupArpFlows() {
+		OFMatch match = new OFMatch();
+		match.setDataLayerType(Ethernet.TYPE_ARP);
+		match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+		
+		OFFlowMod fm = new OFFlowMod();
+		fm.setMatch(match);
+		
+		OFActionOutput action = new OFActionOutput();
+		action.setPort(OFPort.OFPP_CONTROLLER.getValue());
+		action.setMaxLength((short)0xffff);
+		List<OFAction> actions = new ArrayList<OFAction>(1);
+		actions.add(action);
+		fm.setActions(actions);
+		
+		fm.setIdleTimeout((short)0)
+        .setHardTimeout((short)0)
+        .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+        .setCookie(0)
+        .setCommand(OFFlowMod.OFPFC_ADD)
+        .setPriority(ARP_PRIORITY)
+		.setLengthU(OFFlowMod.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH);
+		
+		for (String strdpid : switches){
+			flowCache.write(HexString.toLong(strdpid), fm);
+		}
+	}
+	
+	private void setupDefaultDropFlows() {
+		OFFlowMod fm = new OFFlowMod();
+		fm.setMatch(new OFMatch());
+		fm.setActions(new ArrayList<OFAction>()); //No action means drop
+		
+		fm.setIdleTimeout((short)0)
+        .setHardTimeout((short)0)
+        .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+        .setCookie(0)
+        .setCommand(OFFlowMod.OFPFC_ADD)
+        .setPriority((short)0)
+		.setLengthU(OFFlowMod.MINIMUM_LENGTH);
+		
+		OFFlowMod fmLLDP;
+		OFFlowMod fmBDDP;
+		try {
+			 fmLLDP = fm.clone();
+			 fmBDDP = fm.clone();
+		} catch (CloneNotSupportedException e1) {
+			log.error("Error cloning flow mod", e1);
+			return;
+		}
+		
+		OFMatch matchLLDP = new OFMatch();
+		matchLLDP.setDataLayerType((short)0x8942);
+		matchLLDP.setWildcards(matchLLDP.getWildcards() & ~ OFMatch.OFPFW_DL_TYPE);
+		fmLLDP.setMatch(matchLLDP);
+		
+		OFMatch matchBDDP = new OFMatch();
+		matchBDDP.setDataLayerType((short)0x88cc);
+		matchBDDP.setWildcards(matchBDDP.getWildcards() & ~ OFMatch.OFPFW_DL_TYPE);
+		fmBDDP.setMatch(matchBDDP);
+		
+		OFActionOutput action = new OFActionOutput();
+		action.setPort(OFPort.OFPP_CONTROLLER.getValue());
+		action.setMaxLength((short)0xffff);
+		List<OFAction> actions = new ArrayList<OFAction>(1);
+		actions.add(action);
+		
+		fmLLDP.setActions(actions);
+		fmBDDP.setActions(actions);
+		
+		fmLLDP.setPriority(ARP_PRIORITY);
+		fmLLDP.setLengthU(OFFlowMod.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH);
+		fmBDDP.setPriority(ARP_PRIORITY);
+		fmBDDP.setLengthU(OFFlowMod.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH);
+		
+		List<OFFlowMod> flowModList = new ArrayList<OFFlowMod>(3); 
+		flowModList.add(fm);
+		flowModList.add(fmLLDP);
+		flowModList.add(fmBDDP);
+		
+		for (String strdpid : switches){
+			flowCache.write(HexString.toLong(strdpid), flowModList);
+		}
+	}
+	
 	private void beginRouting(){
 		log.debug("Topology is now ready, beginning routing function");
+		topoRouteTopology = topoRouteService.prepareShortestPathTopo();
+		
+		setupArpFlows();
+		setupDefaultDropFlows();
+		
 		setupBgpPaths();
 		setupFullMesh();
 		
-		//Traverse ptree and create flows for all routes
-		for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)){
-			if (node.rib != null){
-				prefixAdded(node);
-			}
+		//Suppress link discovery on external-facing router ports
+		for (Interface intf : interfaces.values()) {
+			linkDiscoveryService.AddToSuppressLLDPs(intf.getDpid(), intf.getPort());
 		}
+		
+		bgpUpdatesExecutor.execute(new Runnable() {
+			@Override
+			public void run() {
+				doUpdatesThread();
+			}
+		});
 	}
 	
 	private void checkSwitchesConnected(){
@@ -893,8 +1100,6 @@
 	}
 	
 	private void checkStatus(){
-		log.debug("In checkStatus, swC {}, toRe {}", switchesConnected, topologyReady);
-		
 		if (!switchesConnected){
 			checkSwitchesConnected();
 		}
@@ -906,29 +1111,46 @@
 			beginRouting();
 		}
 	}
-	
-	@Override
-	public void startUp(FloodlightModuleContext context) {
-		restApi.addRestletRoutable(new BgpRouteWebRoutable());
-		floodlightProvider.addOFSwitchListener(this);
-		topology.addListener(this);
-		
-		floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
-		
-		//Retrieve the RIB from BGPd during startup
-		retrieveRib();
+
+	private void doUpdatesThread() {
+		boolean interrupted = false;
+		try {
+			while (true) {
+				try {
+					RibUpdate update = ribUpdates.take();
+					switch (update.getOperation()){
+					case UPDATE:
+						processRibAdd(update);
+						break;
+					case DELETE:
+						processRibDelete(update);
+						break;
+					}
+				} catch (InterruptedException e) {
+					log.debug("Interrupted while taking from updates queue", e);
+					interrupted = true;
+				} catch (Exception e) {
+					log.debug("exception", e);
+				}
+			}
+		} finally {
+			if (interrupted) {
+				Thread.currentThread().interrupt();
+			}
+		}
 	}
 
 	@Override
-	public void topologyChanged() {		
-		//There seems to be more topology events than there should be. Lots of link
-		//updated, port up and switch updated on what should be a fairly static topology
+	public void topologyChanged() {
+		if (topologyReady) {
+			return;
+		}
 		
 		boolean refreshNeeded = false;
 		for (LDUpdate ldu : topology.getLastLinkUpdates()){
 			if (!ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_UPDATED)){
 				//We don't need to recalculate anything for just link updates
-				//They happen way too frequently (may be a bug in our link discovery)
+				//They happen very frequently
 				refreshNeeded = true;
 			}
 			
@@ -941,21 +1163,22 @@
 			}
 		}
 		
-		if (refreshNeeded){
+		if (refreshNeeded && !topologyReady){
 			topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
 		}
 	}
 
-	//TODO determine whether we need to listen for switch joins
 	@Override
 	public void addedSwitch(IOFSwitch sw) {
-		//checkStatus();
+		if (!topologyReady) {
+			sw.clearAllFlowMods();
+		}
+		
+		flowCache.switchConnected(sw);
 	}
 
 	@Override
-	public void removedSwitch(IOFSwitch sw) {
-		// TODO Auto-generated method stub	
-	}
+	public void removedSwitch(IOFSwitch sw) {}
 
 	@Override
 	public void switchPortChanged(Long switchId) {}
@@ -964,4 +1187,68 @@
 	public String getName() {
 		return "BgpRoute";
 	}
+	
+	/*
+	 * ILayer3InfoService methods
+	 */
+	
+	@Override
+	public boolean isInterfaceAddress(InetAddress address) {
+		Interface intf = interfacePtrie.match(new Prefix(address.getAddress(), 32));
+		return (intf != null && intf.getIpAddress().equals(address));
+	}
+	
+	@Override
+	public boolean inConnectedNetwork(InetAddress address) {
+		Interface intf = interfacePtrie.match(new Prefix(address.getAddress(), 32));
+		return (intf != null && !intf.getIpAddress().equals(address));
+	}
+	
+	@Override
+	public boolean fromExternalNetwork(long inDpid, short inPort) {
+		for (Interface intf : interfaces.values()) {
+			if (intf.getDpid() == inDpid && intf.getPort() == inPort) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	@Override
+	public Interface getOutgoingInterface(InetAddress dstIpAddress) {
+		return interfacePtrie.match(new Prefix(dstIpAddress.getAddress(), 32));
+	}
+	
+	@Override
+	public boolean hasLayer3Configuration() {
+		return !interfaces.isEmpty();
+	}
+	
+	@Override
+	public MACAddress getRouterMacAddress() {
+		return bgpdMacAddress;
+	}
+
+	/*
+	 * TODO This is a hack to get the REST API to work for ProxyArpManager.
+	 * The REST API is currently tied to the Floodlight module system and we
+	 * need to separate it to allow ONOS modules to use it. For now we will 
+	 * proxy calls through to the ProxyArpManager (which is not a Floodlight 
+	 * module) through this class which is a module.
+	 */
+	@Override
+	public MACAddress getMacAddress(InetAddress ipAddress) {
+		return proxyArp.getMacAddress(ipAddress);
+	}
+
+	@Override
+	public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
+			boolean retry) {
+		proxyArp.sendArpRequest(ipAddress, requester, retry);		
+	}
+
+	@Override
+	public List<String> getMappings() {
+		return proxyArp.getMappings();
+	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
index 8355308..8403f71 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
@@ -1,6 +1,8 @@
 package net.onrc.onos.ofcontroller.bgproute;
 
-import java.net.UnknownHostException;
+import java.util.Iterator;
+
+import net.onrc.onos.ofcontroller.bgproute.RibUpdate.Operation;
 
 import org.restlet.resource.Delete;
 import org.restlet.resource.Get;
@@ -13,20 +15,6 @@
 
 	protected static Logger log = LoggerFactory.getLogger(BgpRouteResource.class);
 
-	private String addrToString(byte [] addr) {
-		String str = "";
-
-		for (int i = 0; i < 4; i++) {
-			int val = (addr[i] & 0xff);
-			str += val;
-			if (i != 3)
-				str += ".";
-		}
-
-		return str;
-	}
-
-	//@SuppressWarnings("unused")
 	@Get
 	public String get(String fmJson) {
 		String dest = (String) getRequestAttributes().get("dest");
@@ -36,72 +24,50 @@
 
 		if (dest != null) {
 			//TODO Needs to be changed to use the new RestClient.get().
-			
-			
-			//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);
+			//Doesn't actually do anything with the response
+			RestClient.get(url); 
 			
 			output="Get rib from bgpd finished!\n";
 			return output;
 		} 
 		else {
-			Ptree ptree = bgpRoute.getPtree();
+			IPatriciaTrie<RibEntry> 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;
+			synchronized(ptree) {
+				Iterator<IPatriciaTrie.Entry<RibEntry>> it = ptree.iterator();
+				while (it.hasNext()) {
+					IPatriciaTrie.Entry<RibEntry> entry = it.next();
+					
+					if (printed == true) {
+						output += ",\n";
+					}
+					
+					output += "    {\"prefix\": \"" + entry.getPrefix() +"\", ";
+					output += "\"nexthop\": \"" + entry.getValue().getNextHop().getHostAddress() +"\"}";
+					
+					printed = true;
 				}
-				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;
 	}
 
-	//unused?
-	/*
-	public static ByteBuffer toByteBuffer(String value) throws UnsupportedEncodingException {
-		return ByteBuffer.wrap(value.getBytes("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 routerId = (String) getRequestAttributes().get("routerid");
 		String prefix = (String) getRequestAttributes().get("prefix");
 		String mask = (String) getRequestAttributes().get("mask");
@@ -119,22 +85,15 @@
 				reply = "[POST: mask format is wrong]";
 				log.info(reply);
 				return reply + "\n";				
-			} catch (UnknownHostException e1) {
+			} catch (IllegalArgumentException 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(routerId, nexthop, p.masklen);
+			RibEntry rib = new RibEntry(routerId, nexthop);
 
-			if (node.rib != null) {
-				node.rib = null;
-				ptree.delReference(node);
-			}
-			node.rib = rib;
-
-			bgpRoute.prefixAdded(node);
+			bgpRoute.newRibUpdate(new RibUpdate(Operation.UPDATE, p, rib));
 			
 			reply = "[POST: " + prefix + "/" + mask + ":" + nexthop + "]";
 			log.info(reply);
@@ -158,8 +117,6 @@
 		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");
@@ -177,39 +134,22 @@
 				reply = "[DELE: mask format is wrong]";
 				log.info(reply);
 				return reply + "\n";
-			} catch (UnknownHostException e1) {
+			} catch (IllegalArgumentException e1) {
 				reply = "[DELE: prefix format is wrong]";
 				log.info(reply);
 				return reply + "\n";
 			}
-
-			PtreeNode node = ptree.lookup(p.getAddress(), p.masklen);
 			
-			//Remove the flows from the switches before the rib is lost
-			//Theory: we could get a delete for a prefix not in the Ptree.
-			//This would result in a null node being returned. We could get a delete for
-			//a node that's not actually there, but is a aggregate node. This would result
-			//in a non-null node with a null rib. Only a non-null node with a non-null
-			//rib is an actual prefix in the Ptree.
-			if (node != null && node.rib != null){
-				bgpRoute.prefixDeleted(node);
-			}
-
-			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);					
-				}
-			}
+			RibEntry r = new RibEntry(routerId, nextHop);
+			
+			bgpRoute.newRibUpdate(new RibUpdate(Operation.DELETE, p, r));
 			
 			reply =reply + "[DELE: " + prefix + "/" + mask + ":" + nextHop + "]";
 		}
 		else {
 			// clear the local rib: Ptree			
 			bgpRoute.clearPtree();
-			reply = "[DELE-capability: " + capability + "; The local Rib is cleared!]\n";
+			reply = "[DELE-capability: " + capability + "; The local RibEntry is cleared!]\n";
 
 			// to store the number in the top node of the Ptree	
 		}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
index c3c8cbb..1d90edc 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Configuration.java
@@ -1,17 +1,20 @@
 package net.onrc.onos.ofcontroller.bgproute;
 
+import java.util.Collections;
 import java.util.List;
 
+import net.floodlightcontroller.util.MACAddress;
+
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.openflow.util.HexString;
 
 public class Configuration {
 	private long bgpdAttachmentDpid;
 	private short bgpdAttachmentPort;
+	private MACAddress bgpdMacAddress;
 	private List<String> switches;
 	private List<Interface> interfaces;
 	private List<BgpPeer> peers;
-	//private Map<String, GatewayRouter> gateways;
 	
 	public Configuration() {
 		// TODO Auto-generated constructor stub
@@ -34,9 +37,18 @@
 	public void setBgpdAttachmentPort(short bgpdAttachmentPort) {
 		this.bgpdAttachmentPort = bgpdAttachmentPort;
 	}
+	
+	public MACAddress getBgpdMacAddress() {
+		return bgpdMacAddress;
+	}
+
+	@JsonProperty("bgpdMacAddress")
+	public void setBgpdMacAddress(String strMacAddress) {
+		this.bgpdMacAddress = MACAddress.valueOf(strMacAddress);
+	}
 
 	public List<String> getSwitches() {
-		return switches;
+		return Collections.unmodifiableList(switches);
 	}
 
 	@JsonProperty("switches")
@@ -45,7 +57,7 @@
 	}
 
 	public List<Interface> getInterfaces() {
-		return interfaces;
+		return Collections.unmodifiableList(interfaces);
 	}
 
 	@JsonProperty("interfaces")
@@ -54,7 +66,7 @@
 	}
 	
 	public List<BgpPeer> getPeers() {
-		return peers;
+		return Collections.unmodifiableList(peers);
 	}
 
 	@JsonProperty("bgpPeers")
@@ -62,14 +74,4 @@
 		this.peers = peers;
 	}
 
-	/*
-	public Map<String, GatewayRouter> getGateways() {
-		return gateways;
-	}
-
-	@JsonProperty("gateways")
-	public void setGateways(Map<String, GatewayRouter> gateways) {
-		this.gateways = gateways;
-	}*/
-
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/FlowCache.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/FlowCache.java
new file mode 100644
index 0000000..d1cb578
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/FlowCache.java
@@ -0,0 +1,158 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFSwitch;
+
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPort;
+import org.openflow.util.HexString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FlowCache {
+	private static Logger log = LoggerFactory.getLogger(FlowCache.class);
+	
+	private IFloodlightProviderService floodlightProvider;
+	
+	private Map<Long, List<OFFlowMod>> flowCache;
+	
+	private Comparator<OFFlowMod> cookieComparator = new Comparator<OFFlowMod>() {
+		@Override
+		public int compare(OFFlowMod fm1, OFFlowMod fm2) {
+			long difference = fm2.getCookie() - fm1.getCookie(); 
+			
+			if (difference > 0) {
+				return 1;
+			}
+			else if (difference < 0) {
+				return -1;
+			}
+			else {
+				return 0;
+			}
+		}
+	};
+	
+	public FlowCache(IFloodlightProviderService floodlightProvider) {
+		this.floodlightProvider = floodlightProvider;
+		
+		flowCache = new HashMap<Long, List<OFFlowMod>>();
+	}
+
+	public synchronized void write(long dpid, OFFlowMod flowMod) {
+		List<OFFlowMod> flowModList = new ArrayList<OFFlowMod>(1);
+		flowModList.add(flowMod);
+		write(dpid, flowModList);
+	}
+	
+	public synchronized void write(long dpid, List<OFFlowMod> flowMods) {
+		ensureCacheForSwitch(dpid);
+		
+		List<OFFlowMod> clones = new ArrayList<OFFlowMod>(flowMods.size());
+		
+		//Somehow the OFFlowMods we get passed in will change later on.
+		//No idea how this happens, but we can just clone to prevent problems
+		try {
+			for (OFFlowMod fm : flowMods) {
+				clones.add(fm.clone());
+			}
+		} catch (CloneNotSupportedException e) {
+			log.debug("Clone exception", e);
+		}
+		
+		flowCache.get(dpid).addAll(clones);
+		
+		IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+		
+		if (sw == null) {
+			log.debug("Switch not found when writing flow mods");
+			return;
+		}
+
+		List<OFMessage> msgList = new ArrayList<OFMessage>(clones.size());
+		msgList.addAll(clones);
+		
+		try {
+			sw.write(msgList, null);
+		} catch (IOException e) {
+			log.error("Error writing to switch", e);
+		}
+		
+
+	}
+	
+	public synchronized void delete(long dpid, OFFlowMod flowMod) {
+		List<OFFlowMod> flowModList = new ArrayList<OFFlowMod>(1);
+		flowModList.add(flowMod);
+		delete(dpid, flowModList);
+	}
+	
+	public synchronized void delete(long dpid, List<OFFlowMod> flowMods) {
+		ensureCacheForSwitch(dpid);
+		
+		//Remove the flow mods from the cache first before we alter them
+		flowCache.get(dpid).removeAll(flowMods);
+		
+		//Alter the original flow mods to make them delete flow mods
+		for (OFFlowMod fm : flowMods) {
+			fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT)
+			.setOutPort(OFPort.OFPP_NONE)
+			.setLengthU(OFFlowMod.MINIMUM_LENGTH);
+			
+			fm.getActions().clear();
+		}
+		
+		IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+		if (sw == null) {
+			log.debug("Switch not found when writing flow mods");
+			return;
+		}
+		
+		List<OFMessage> msgList = new ArrayList<OFMessage>(flowMods.size());
+		msgList.addAll(flowMods);
+		
+		try {
+			sw.write(msgList, null);
+		} catch (IOException e) {
+			log.error("Error writing to switch", e);
+		}
+	}
+	
+	//TODO can the Prontos handle being sent all flow mods in one message?
+	public synchronized void switchConnected(IOFSwitch sw) {
+		log.debug("Switch connected: {}", sw);
+		
+		ensureCacheForSwitch(sw.getId());
+		
+		List<OFFlowMod> flowMods = flowCache.get(sw.getId());
+
+		Collections.sort(flowMods, cookieComparator);
+		
+		sw.clearAllFlowMods();
+		
+		List<OFMessage> messages = new ArrayList<OFMessage>(flowMods.size());
+		messages.addAll(flowMods);
+		
+		try {
+			sw.write(messages, null);
+		} catch (IOException e) {
+			log.error("Failure writing flow mods to switch {}",
+					HexString.toHexString(sw.getId()));
+		}		
+	}
+	
+	private void ensureCacheForSwitch(long dpid) {
+		if (!flowCache.containsKey(dpid)) {
+			flowCache.put(dpid, new ArrayList<OFFlowMod>());
+		}
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/GatewayRouter.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/GatewayRouter.java
deleted file mode 100644
index e893acf..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/GatewayRouter.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package net.onrc.onos.ofcontroller.bgproute;
-
-import net.floodlightcontroller.util.MACAddress;
-import net.onrc.onos.ofcontroller.util.Dpid;
-import net.onrc.onos.ofcontroller.util.IPv4;
-import net.onrc.onos.ofcontroller.util.Port;
-import net.onrc.onos.ofcontroller.util.SwitchPort;
-
-import org.codehaus.jackson.annotate.JsonProperty;
-import org.openflow.util.HexString;
-
-public class GatewayRouter {
-	private SwitchPort attachmentPoint = null;
-	private long dpid;
-	private short port;
-	private MACAddress routerMac;
-	private IPv4 routerIp;
-	private IPv4 myIpAddress;
-	
-	
-	public SwitchPort getAttachmentPoint() {
-		if (attachmentPoint == null){
-			attachmentPoint = new SwitchPort(new Dpid(dpid), new Port(port));
-		}
-		return attachmentPoint;
-	}
-	
-	public long getDpid() {
-		return dpid;
-	}
-
-	@JsonProperty("attachmentDpid")
-	public void setDpid(String dpid) {
-		this.dpid = HexString.toLong(dpid);
-	}
-
-	public short getPort() {
-		return port;
-	}
-
-	@JsonProperty("attachmentPort")
-	public void setPort(short port) {
-		this.port = port;
-	}
-
-	public MACAddress getRouterMac() {
-		return routerMac;
-	}
-	
-	@JsonProperty("macAddress")
-	public void setRouterMac(String routerMac) {
-		this.routerMac = MACAddress.valueOf(routerMac);;
-	}
-
-	public IPv4 getRouterIp() {
-		return routerIp;
-	}
-	
-	@JsonProperty("ipAddress")
-	public void setRouterIp(String routerIp) {
-		this.routerIp = new IPv4(routerIp);
-	}
-	
-	public IPv4 getMyIpAddress() {
-		return myIpAddress;
-	}
-	
-	@JsonProperty("myIpAddress")
-	public void setMyIpAddress(String myIpAddress) {
-		this.myIpAddress = new IPv4(myIpAddress);
-	}
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
index c84a415..954976c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
@@ -4,9 +4,10 @@
 
 public interface IBgpRouteService extends IFloodlightService {
 
-	public Rib lookupRib(byte[] dest);
+	//public RibEntry lookupRib(byte[] dest);
 
-	public Ptree getPtree();
+	//public Ptree getPtree();
+	public IPatriciaTrie<RibEntry> getPtree();
 
 	public String getBGPdRestIp();
 
@@ -14,7 +15,13 @@
 
 	public void clearPtree();
 	
+	/**
+	 * Pass a RIB update to the {@link IBgpRouteService}
+	 * @param update
+	 */
+	public void newRibUpdate(RibUpdate update);
+	
 	//TODO This functionality should be provided by some sort of Ptree listener framework
-	public void prefixAdded(PtreeNode node);
-	public void prefixDeleted(PtreeNode node);
+	//public void prefixAdded(PtreeNode node);
+	//public void prefixDeleted(PtreeNode node);
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/ILayer3InfoService.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/ILayer3InfoService.java
new file mode 100644
index 0000000..00ddd68
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/ILayer3InfoService.java
@@ -0,0 +1,35 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.net.InetAddress;
+
+import net.floodlightcontroller.util.MACAddress;
+
+/**
+ * Provides information about the layer 3 properties of the network.
+ * This is based on IP addresses configured on ports in the network.
+ *
+ */
+public interface ILayer3InfoService {
+	public boolean isInterfaceAddress(InetAddress address);
+	public boolean inConnectedNetwork(InetAddress address);
+	public boolean fromExternalNetwork(long inDpid, short inPort);
+	
+	/**
+	 * Retrieves the {@link Interface} object for the interface that packets
+	 * to dstIpAddress will be sent out of. Returns null if dstIpAddress is not
+	 * in a directly connected network, or if no interfaces are configured.
+	 * @param dstIpAddress Destination IP address that we want to match to
+	 * an outgoing interface
+	 * @return The {@link Interface} object if found, null if not
+	 */
+	public Interface getOutgoingInterface(InetAddress dstIpAddress);
+	
+	/**
+	 * Returns whether this controller has a layer 3 configuration 
+	 * (i.e. interfaces and IP addresses)
+	 * @return True if IP addresses are configured, false if not
+	 */
+	public boolean hasLayer3Configuration();
+	
+	public MACAddress getRouterMacAddress();
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java
new file mode 100644
index 0000000..1fb0716
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java
@@ -0,0 +1,20 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.util.Iterator;
+
+public interface IPatriciaTrie<V> {
+	public V put(Prefix prefix, V value);
+	
+	public V lookup(Prefix prefix);
+	
+	public V match(Prefix prefix);
+	
+	public boolean remove(Prefix prefix, V value);
+	
+	public Iterator<Entry<V>> iterator();
+	
+	interface Entry<V> {
+		public Prefix getPrefix();
+		public V getValue();
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
index 088c18e..48b60d8 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
@@ -6,68 +6,57 @@
 import net.onrc.onos.ofcontroller.util.Port;
 import net.onrc.onos.ofcontroller.util.SwitchPort;
 
+import org.codehaus.jackson.annotate.JsonCreator;
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.openflow.util.HexString;
 
 import com.google.common.net.InetAddresses;
 
 public class Interface {
-	private String name;
-	private SwitchPort switchPort = null;
-	private long dpid;
-	private short port;
-	private InetAddress ipAddress;
-	private int prefixLength;
+	private final String name;
+	private final SwitchPort switchPort;
+	private final long dpid;
+	private final short port;
+	private final InetAddress ipAddress;
+	private final int prefixLength;
+	
+	@JsonCreator
+	public Interface (@JsonProperty("name") String name,
+					  @JsonProperty("dpid") String dpid,
+					  @JsonProperty("port") short port,
+					  @JsonProperty("ipAddress") String ipAddress,
+					  @JsonProperty("prefixLength") int prefixLength) {
+		this.name = name;
+		this.dpid = HexString.toLong(dpid);
+		this.port = port;
+		this.ipAddress = InetAddresses.forString(ipAddress);
+		this.prefixLength = prefixLength;
+		this.switchPort = new SwitchPort(new Dpid(this.dpid), new Port(this.port));
+	}
 	
 	public String getName() {
 		return name;
 	}
 
-	@JsonProperty("name")
-	public void setName(String name) {
-		this.name = name;
-	}
-
-	public synchronized SwitchPort getSwitchPort() {
-		if (switchPort == null){
-			switchPort = new SwitchPort(new Dpid(dpid), new Port(port));
-		}
-		return switchPort;
+	public SwitchPort getSwitchPort() {
+		//TODO SwitchPort, Dpid and Port are mutable, but they could probably
+		//be made immutable which would prevent the need to copy
+		return new SwitchPort(new Dpid(dpid), new Port(port));
 	}
 	
 	public long getDpid() {
 		return dpid;
 	}
 
-	@JsonProperty("dpid")
-	public void setDpid(String dpid) {
-		this.dpid = HexString.toLong(dpid);
-	}
-
 	public short getPort() {
 		return port;
 	}
 
-	@JsonProperty("port")
-	public void setPort(short port) {
-		this.port = port;
-	}
-
 	public InetAddress getIpAddress() {
 		return ipAddress;
 	}
 
-	@JsonProperty("ipAddress")
-	public void setIpAddress(String ipAddress) {
-		this.ipAddress = InetAddresses.forString(ipAddress);
-	}
-
 	public int getPrefixLength() {
 		return prefixLength;
 	}
-
-	@JsonProperty("prefixLength")
-	public void setPrefixLength(int prefixLength) {
-		this.prefixLength = prefixLength;
-	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Path.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Path.java
new file mode 100644
index 0000000..5cf4b09
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Path.java
@@ -0,0 +1,61 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.net.InetAddress;
+import java.util.Collections;
+import java.util.List;
+
+/*
+ * A path is always assumed to be from all other interfaces (external-facing
+ * switchports) to the destination interface.
+ */
+
+public class Path {
+
+	private Interface dstInterface;
+	private InetAddress dstIpAddress;
+	private int numUsers = 0;
+	
+	private List<PushedFlowMod> flowMods = null;
+	private boolean permanent = false;
+	
+	public Path(Interface dstInterface, InetAddress dstIpAddress) {
+		this.dstInterface = dstInterface;
+		this.dstIpAddress = dstIpAddress;
+	}
+
+	public Interface getDstInterface() {
+		return dstInterface;
+	}
+
+	public InetAddress getDstIpAddress() {
+		return dstIpAddress;
+	}
+	
+	public void incrementUsers() {
+		numUsers++;
+	}
+	
+	public void decrementUsers() {
+		numUsers--;
+	}
+	
+	public int getUsers() {
+		return numUsers;
+	}
+	
+	public List<PushedFlowMod> getFlowMods() {
+		return Collections.unmodifiableList(flowMods);
+	}
+	
+	public void setFlowMods(List<PushedFlowMod> flowMods) {
+		this.flowMods = flowMods;
+	}
+	
+	public boolean isPermanent() {
+		return permanent;
+	}
+	
+	public void setPermanent() {
+		permanent = true;
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java
new file mode 100644
index 0000000..89dfb30
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java
@@ -0,0 +1,506 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class PatriciaTrie<V> implements IPatriciaTrie<V> {
+	private final byte maskBits[] = {(byte)0x00, (byte)0x80, (byte)0xc0, (byte)0xe0, (byte)0xf0, 
+												 (byte)0xf8, (byte)0xfc, (byte)0xfe, (byte)0xff};
+	
+	private int maxPrefixLength;
+	
+	private Node top;
+
+	public PatriciaTrie(int maxPrefixLength) {
+		this.maxPrefixLength = maxPrefixLength;
+	}
+
+	@Override
+	public synchronized V put(Prefix prefix, V value) {
+		if (prefix.getPrefixLength() > maxPrefixLength) {
+			throw new IllegalArgumentException(String.format(
+					"Prefix length %d is greater than max prefix length %d", 
+					prefix.getPrefixLength(), maxPrefixLength));
+		}
+		
+		if (prefix == null || value == null) {
+			throw new NullPointerException();
+		}
+		
+		Node node = top;
+		Node match = null;
+		
+		while (node != null
+				&& node.prefix.getPrefixLength() <= prefix.getPrefixLength()
+				&& key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
+		    if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
+		    	/*
+		    	 * Prefix is already in tree. This may be an aggregate node, in which case
+		    	 * we are inserting a new prefix, or it could be an actual node, in which 
+		    	 * case we are inserting a new nexthop for the prefix and should return
+		    	 * the old nexthop.
+		    	 */
+		    	V oldValue = node.value;
+		    	node.value = value;
+		    	return oldValue;
+			}
+
+			match = node;
+			
+			if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
+				node = node.right;
+			} else {
+				node = node.left;
+			}
+		}
+
+		Node add = null;
+		
+		if (node == null) {
+			//add = new Node(p, r);
+			add = new Node(prefix);
+			add.value = value;
+			
+			if (match != null) {
+				node_link(match, add);
+			} else {
+				top = add;
+			}
+		} else {
+			add = node_common(node, prefix.getAddress(), prefix.getPrefixLength());
+			if (add == null) {
+				//I think this is -ENOMEM?
+				//return null;
+			}				
+			
+			if (match != null) {
+				node_link(match, add);
+			} else {
+				top = add;
+			}
+			node_link(add, node);
+			
+			if (add.prefix.getPrefixLength() != prefix.getPrefixLength()) {
+				match = add;
+				
+				//add = new Node(p, r);
+				add = new Node(prefix);
+				add.value = value;
+				node_link(match, add);
+			}
+			else {
+				add.value = value;
+			}
+		}
+		
+		//If we added a new Node, there was no previous mapping
+		return null;
+		//return addReference(add);
+	}
+	
+	/*exact match*/
+	@Override
+	public synchronized V lookup(Prefix prefix) {
+		if (prefix.getPrefixLength() > maxPrefixLength) {
+			return null;
+		}
+		
+		/*
+		Node node = top;
+		
+		while (node != null
+				&& node.prefix.getPrefixLength() <= p.getPrefixLength()
+				&& key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
+			if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
+				//return addReference(node);
+				return node.rib;
+			}
+			
+			if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
+				node = node.right;
+			} else {
+				node = node.left;
+			}
+		}
+		*/
+		
+		Node node = findNode(prefix);
+		
+		return node == null ? null : node.value;
+	}
+	
+	/*closest containing prefix*/
+	@Override
+	public synchronized V match(Prefix prefix) {
+		//TODO
+		if (prefix.getPrefixLength() > maxPrefixLength) {
+			return null;
+		}
+		
+		Node closestNode = findClosestNode(prefix);
+		
+		return closestNode == null ? null : closestNode.value;
+	}
+	
+	@Override
+	public synchronized boolean remove(Prefix prefix, V value) {
+		Node child;
+		Node parent;
+		
+		if (prefix == null || value == null) {
+			return false;
+		}
+		
+		Node node = findNode(prefix);
+		
+		if (node == null || node.isAggregate() || !node.value.equals(value)) {
+			//Given <prefix, nexthop> mapping is not in the tree
+			return false;
+		}
+		
+		if (node.left != null && node.right != null) {
+			//Remove the RibEntry entry and leave this node as an aggregate node
+			//In the future, maybe we should re-evaluate what the aggregate prefix should be?
+			//It shouldn't necessarily stay the same.
+			//More complicated if the above prefix is also aggregate.
+			node.value = null;
+			return true;
+		}
+		
+		if (node.left != null) {
+			child = node.left;
+		} else {
+			child = node.right;
+		}
+		
+		parent = node.parent;
+		
+		if (child != null) {
+			child.parent = parent;
+		}
+		
+		if (parent != null) {
+			if (parent.left == node) {
+				parent.left = child;
+			} else {
+				parent.right = child;
+			}
+		} else {
+			top = child;
+		}
+		
+		/*
+		 * TODO not sure what to do here. I think this is lazily deleting aggregate nodes,
+		 * notice that it used to do nothing if it detected both children were not null earlier.
+		 * But here, what we really should do is reevaluate the aggregate prefix of the parent
+		 * node (if it is indeed an aggregate). Because at the moment, no aggregate node will ever
+		 * be removed. BUT, I don't actually think this presents a correctness problem, at
+		 * least from an external point of view.
+		 */
+		//if (parent != null && parent.refCount == 0) {
+			//node_remove(parent);
+		//}
+		
+		return true;
+	}
+	
+	@Override
+	public Iterator<Entry<V>> iterator() {
+		return new PatriciaTrieIterator(top);
+	}
+	
+	private Node findNode(Prefix prefix) {
+		Node node = top;
+		
+		while (node != null
+				&& node.prefix.getPrefixLength() <= prefix.getPrefixLength()
+				&& key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
+			if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
+				//return addReference(node);
+				return node;
+			}
+			
+			if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
+				node = node.right;
+			} else {
+				node = node.left;
+			}
+		}
+		
+		return null;
+	}
+	
+	private Node findClosestNode(Prefix prefix) {
+		Node node = top;
+		Node match = null;
+		
+		while (node != null
+				&& node.prefix.getPrefixLength() <= prefix.getPrefixLength()
+				&& key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
+			if (!node.isAggregate()) {
+				match = node;
+			}
+			
+			if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
+				node = node.right;
+			} else {
+				node = node.left;
+			}
+		}
+		
+		return match;
+	}
+	
+	/*
+	 * Receives a 1-based bit index
+	 * Returns a 1-based byte index
+	 * eg. (0 => 1), 1 => 1, 8 => 1, 9 => 2, 17 => 3
+	 */
+	private int getByteContainingBit(int bitNumber) {
+		return Math.max((bitNumber + 7) / 8, 1);
+	}
+	
+	private boolean key_match(byte [] key1, int key1_len, byte [] key2, int key2_len) {
+		//int offset;
+		//int shift;
+		
+		if (key1_len > key2_len) {
+			return false;
+		}
+		
+		int offset = (Math.min(key1_len, key2_len)) / 8;
+		int shift = (Math.min(key1_len, key2_len)) % 8;
+		
+		if (shift != 0) {
+			if ((maskBits[shift] & (key1[offset] ^ key2[offset])) != 0) {
+				return false;
+			}
+		}
+		
+		while (offset != 0) {
+			offset--;
+			if (key1[offset] != key2[offset]) {
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	private boolean bit_check(byte [] key, int key_bits) {
+		int offset = key_bits / 8;
+		int shift = 7 - (key_bits % 8);
+		int bit = key[offset] & 0xff;
+
+		bit >>= shift;
+		
+		if ((bit & 1) == 1) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+	
+	private void node_link(Node node, Node add) {
+		boolean bit = bit_check(add.prefix.getAddress(), node.prefix.getPrefixLength());
+		
+		if (bit == true) {
+			node.right = add;
+		} else {
+			node.left = add;
+		}
+		add.parent = node;
+	}
+	
+    private Node node_common(Node node, byte [] key, int key_bits) {
+		int i;
+		int limit = Math.min(node.prefix.getPrefixLength(), key_bits) / 8;
+
+		for (i = 0; i < limit; i++) {
+			if (node.prefix.getAddress()[i] != key[i]) {
+				break;
+			}
+		}
+		
+		int common_len = i * 8;
+		int boundary = 0;
+
+		if (common_len != key_bits) {
+			byte diff = (byte)(node.prefix.getAddress()[i] ^ key[i]);
+			byte mask = (byte)0x80;
+			int shift_mask = 0;
+			
+			while (common_len < key_bits && ((mask & diff) == 0)) {
+				boundary = 1;
+
+				shift_mask = (mask & 0xff);
+				shift_mask >>= 1;
+				mask = (byte)shift_mask;
+
+				common_len++;
+			}
+		}
+		
+		//Node add = new Node(null, common_len, maxKeyOctets);
+		//if (add == null)
+			//Another -ENOMEM;
+			//return null;
+		
+		//Creating a new Prefix with a prefix length of common_len
+		//Bits are copied from node's up until the common_len'th bit
+		//RibEntry is null, because this is an aggregate prefix - it's not
+		//actually been added to the trie.
+		
+		byte[] newPrefix = new byte[getByteContainingBit(maxPrefixLength)];
+		
+		int j;
+		for (j = 0; j < i; j++)
+			newPrefix[j] = node.prefix.getAddress()[j];
+
+		if (boundary != 0)
+			newPrefix[j] = (byte)(node.prefix.getAddress()[j] & maskBits[common_len % 8]);
+		
+		//return new Node(new Prefix(newPrefix, common_len), null);
+		return new Node(new Prefix(newPrefix, common_len));
+		//return add;
+	}
+	
+	private class Node {
+		public Node parent = null;
+		public Node left = null;
+		public Node right = null;
+		
+		public final Prefix prefix;
+		public V value;
+		
+		//public Node(Prefix p, RibEntry r) {
+		//	this.prefix = p;
+		//	this.rib = r;
+		//}
+		public Node(Prefix p) {
+			this.prefix = p;
+		}
+		
+		public boolean isAggregate() {
+			return value == null;
+		}
+				
+		public Entry<V> getEntry() {
+			return new PatriciaTrieEntry(prefix, value);
+		}
+	}
+	
+	private class PatriciaTrieEntry implements Entry<V> {
+		private Prefix prefix;
+		private V value;
+		
+		public PatriciaTrieEntry(Prefix prefix, V value) {
+			this.prefix = prefix;
+			this.value = value;
+		}
+		
+		@Override
+		public Prefix getPrefix() {
+			return prefix;
+		}
+		
+		@Override
+		public V getValue() {
+			return value;
+		}
+	}
+	
+	private class PatriciaTrieIterator implements Iterator<Entry<V>> {
+		
+		private Node current;
+		private boolean started = false;
+		
+		public PatriciaTrieIterator(Node start) {
+			current = start;
+			
+			//If the start is an aggregate node fast forward to find the next valid node
+			if (current != null && current.isAggregate()) {
+				current = findNext(current);
+			}
+		}
+
+		@Override
+		public boolean hasNext() {
+			if (current == null) {
+				return false;
+			}
+			
+			if (!started) {
+				return true;
+			}
+			
+			return findNext(current) != null;
+		}
+
+		@Override
+		public Entry<V> next() {
+			if (current == null) {
+				throw new NoSuchElementException();
+			}
+			
+			if (!started) {
+				started = true;
+				return current.getEntry();
+			}
+			
+			current = findNext(current);
+			if (current == null) {
+				throw new NoSuchElementException();
+			}
+			
+			return current.getEntry();
+		}
+
+		@Override
+		public void remove() {
+			// TODO This could be implemented, if it were needed
+			throw new NoSuchElementException();
+		}
+		
+		private Node findNext(Node node) {
+			Node next = null;
+			
+			if (node.left != null) {
+				next = node.left;
+				//addReference(next);
+				//delReference(node);
+				//return next;
+			}
+			else if (node.right != null) {
+				next = node.right;
+				//addReference(next);
+				//delReference(node);
+				//return next;
+			}
+			else {
+				//Node start = node;
+				while (node.parent != null) {
+					if (node.parent.left == node && node.parent.right != null) {
+						next = node.parent.right;
+						//addReference(next);
+						//delReference(start);
+						//return next;
+						break;
+					}
+					node = node.parent;
+				}
+			}
+			
+			if (next == null) {
+				return null;
+			}
+			
+			//If the node doesn't have a value, it's not an actual node, it's an artifically
+			//inserted aggregate node. We don't want to return these to the user.
+			if (next.isAggregate()) {
+				return findNext(next);
+			}
+			
+			return next;
+		}
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
index c3baa37..05ce0a4 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
@@ -2,33 +2,134 @@
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.Arrays;
+
+import com.google.common.net.InetAddresses;
 
 public class Prefix {
-	public int masklen;
-	protected InetAddress address;
+	private final int MAX_BYTES = 4;
+	
+	private final int prefixLength;
+	private final byte[] address;
+	
+	//For verifying the arguments and pretty printing
+	private final InetAddress inetAddress;
+	
+	public Prefix(byte[] addr, int prefixLength) {
+		if (addr == null || addr.length != MAX_BYTES || 
+				prefixLength < 0 || prefixLength > MAX_BYTES * Byte.SIZE) {
+			throw new IllegalArgumentException();
+		}
 
-	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);
+		address = canonicalizeAddress(addr, prefixLength);
+		this.prefixLength = prefixLength;
+		
+		try {
+			inetAddress = InetAddress.getByAddress(address);
+		} catch (UnknownHostException e) {
+			throw new IllegalArgumentException();
+		}
 	}
 
-	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 Prefix(String strAddress, int prefixLength) {
+		byte[] addr = null;
+		addr = InetAddresses.forString(strAddress).getAddress();
+				
+		if (addr == null || addr.length != MAX_BYTES || 
+				prefixLength < 0 || prefixLength > MAX_BYTES * Byte.SIZE) {
+			throw new IllegalArgumentException();
+		}
+		
+		address = canonicalizeAddress(addr, prefixLength);
+		this.prefixLength = prefixLength;
+		
+		try {
+			inetAddress = InetAddress.getByAddress(address);
+		} catch (UnknownHostException e) {
+			throw new IllegalArgumentException();
+		}
+	}
+	
+	private byte[] canonicalizeAddress(byte[] address, int prefixLength) {
+		byte[] result = new byte[address.length];
+		
+		if (prefixLength == 0) {
+			for (int i = 0; i < MAX_BYTES; i++) {
+				result[i] = 0;
+			}
+			
+			return result;
+		}
+		
+		result = Arrays.copyOf(address, address.length);
+		
+		//Set all bytes after the end of the prefix to 0
+		int lastByteIndex = (prefixLength - 1) / Byte.SIZE;
+		for (int i = lastByteIndex; i < MAX_BYTES; i++) {
+			result[i] = 0;
+		}
+		
+		byte lastByte = address[lastByteIndex];
+		byte mask = 0;
+		byte msb = (byte) 0x80;
+		int lastBit = (prefixLength - 1) % Byte.SIZE;
+		for (int i = 0; i < Byte.SIZE; i++) {
+			if (i <= lastBit) {
+				mask |= (msb >> i);
+			}
+		}
+
+		result[lastByteIndex] = (byte) (lastByte & mask);
+		
+		return result;
 	}
 
-	public byte [] getAddress() {
-		return address.getAddress();
+	public int getPrefixLength() {
+		return prefixLength;
+	}
+	
+	public byte[] getAddress() {
+		return address;
+	}
+	
+	@Override
+	public boolean equals(Object other) {
+		if (other == null || !(other instanceof Prefix)) {
+			return false;
+		}
+		
+		Prefix otherPrefix = (Prefix) other;
+		
+		return (Arrays.equals(address, otherPrefix.address)) &&
+				(prefixLength == otherPrefix.prefixLength);
+	}
+	
+	@Override
+	public int hashCode() {
+		int hash = 17;
+		hash = 31 * hash + prefixLength;
+		hash = 31 * hash + Arrays.hashCode(address);
+		return hash;
+	}
+	
+	@Override
+	public String toString() {
+		return inetAddress.getHostAddress() + "/" + prefixLength;
+	}
+	
+	public String printAsBits() {
+		String result = "";
+		for (int i = 0; i < address.length; i++) {
+			byte b = address[i];
+			for (int j = 0; j < Byte.SIZE; j++) {
+				byte mask = (byte) (0x80 >>> j);
+				result += ((b & mask) == 0)? "0" : "1";
+				if (i*Byte.SIZE+j == prefixLength-1) {
+					return result;
+				}
+			}
+			result += " ";
+		}
+		return result.substring(0, result.length() - 1);
 	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Ptree.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Ptree.java
index 6741a41..041061c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Ptree.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Ptree.java
@@ -1,24 +1,33 @@
 package net.onrc.onos.ofcontroller.bgproute;
 
+/*
+ * TODO This Ptree needs to be refactored if we're going to use it permenantly.
+ *
+ * The biggest problem is it leaks PTreeNode references - these need to stay within
+ * the Ptree as they contain data fundamental to the structure of the tree.
+ * You should put RIB entries in and get RIB entries out.
+ * Also we need to get rid of the referencing scheme to determine when to delete nodes.
+ * Deletes should be explicit, and there's no need to keep track of references if 
+ * we don't leak them out the the Ptree.
+ */
 public class Ptree {
-	int maxKeyBits;
-	int maxKeyOctets;
-	int refCount;
-	PtreeNode top;
-	byte maskBits[] = { (byte)0x00, (byte)0x80, (byte)0xc0, (byte)0xe0, (byte)0xf0, (byte)0xf8, (byte)0xfc, (byte)0xfe, (byte)0xff };
+	private int maxKeyBits;
+	private int maxKeyOctets;
+	//private int refCount;
+	private PtreeNode top;
+	private byte maskBits[] = { (byte)0x00, (byte)0x80, (byte)0xc0, (byte)0xe0, (byte)0xf0, (byte)0xf8, (byte)0xfc, (byte)0xfe, (byte)0xff };
 	
-	// Constructor.
-	Ptree(int max_key_bits) {
+	public Ptree(int max_key_bits) {
 		maxKeyBits = max_key_bits;
 		maxKeyOctets = bit_to_octet(max_key_bits); 
-		refCount = 0;
+		//refCount = 0;
 	}
 	
-	public PtreeNode acquire(byte [] key) {
+	public synchronized PtreeNode acquire(byte [] key) {
 		return acquire(key, maxKeyBits);
 	}
 	
-	public PtreeNode acquire(byte [] key, int key_bits) {
+	public synchronized PtreeNode acquire(byte [] key, int key_bits) {
 		if (key_bits > maxKeyBits) {
 			return null;
 		}
@@ -76,7 +85,7 @@
 		return addReference(add);
 	}
 
-	public PtreeNode lookup(byte [] key, int key_bits) {
+	public synchronized PtreeNode lookup(byte [] key, int key_bits) {
 		if (key_bits > maxKeyBits) {
 			return null;
 		}
@@ -99,7 +108,7 @@
 		return null;
 	}
 	
-	public PtreeNode match(byte [] key, int key_bits) {
+	public synchronized PtreeNode match(byte [] key, int key_bits) {
 		if (key_bits > maxKeyBits) {
 			return null;
 		}
@@ -127,14 +136,14 @@
 		return null;
 	}
 	
-	public PtreeNode begin() {
+	public synchronized PtreeNode begin() {
 		if (top == null) {
 			return null;
 		}
 		return addReference(top);
 	}
 	
-	public PtreeNode next(PtreeNode node) {
+	public synchronized PtreeNode next(PtreeNode node) {
 		PtreeNode next;
 		
 		if (node.left != null) {
@@ -175,7 +184,7 @@
 		return node;
 	}
 	
-	public void delReference(PtreeNode node) {
+	public synchronized void delReference(PtreeNode node) {
 		if (node.refCount > 0) {
 			node.refCount--;
 		}
@@ -278,10 +287,6 @@
 		
 		return add;
 	}
-	//add by linpp
-	private void clear() {
-	
-	}
 	
 	private void node_remove(PtreeNode node) {
 		PtreeNode child;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PtreeNode.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PtreeNode.java
index a4d6996..50b7c7d 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PtreeNode.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PtreeNode.java
@@ -13,7 +13,7 @@
 	
 	public int refCount;
 	
-	public Rib rib;
+	public RibEntry rib;
 	protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
 	
 	PtreeNode(byte [] key, int key_bits, int max_key_octet) {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java
new file mode 100644
index 0000000..fd9ba6f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java
@@ -0,0 +1,32 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import org.openflow.protocol.OFFlowMod;
+
+/**
+ * Wraps up a DPID and a OFFlowMod so we know how to delete
+ * the flow if we have to.
+ * 
+ * TODO This functionality should be handled by ONOS's flow layer in future.
+ *
+ */
+public class PushedFlowMod {
+	private long dpid;
+	private OFFlowMod flowMod;
+	
+	public PushedFlowMod(long dpid, OFFlowMod flowMod) {
+		this.dpid = dpid;
+		try {
+			this.flowMod = flowMod.clone();
+		} catch (CloneNotSupportedException e) {
+			this.flowMod = flowMod;
+		}
+	}
+	
+	public long getDpid() {
+		return dpid;
+	}
+	
+	public OFFlowMod getFlowMod() {
+		return flowMod;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Rib.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Rib.java
deleted file mode 100644
index dc5f71d..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Rib.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package net.onrc.onos.ofcontroller.bgproute;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-public class Rib {
-	protected InetAddress routerId;
-	protected InetAddress nextHop;
-	protected int masklen;
-//	protected int distance;
-	
-	Rib(InetAddress router_id, InetAddress nexthop, int masklen) {
-		this.routerId = router_id;
-		this.nextHop = nexthop;
-		this.masklen = masklen;
-//		this.distance = distance;
-	}
-	
-	Rib(String router_id, String nexthop, int masklen) {
-		try {
-			this.routerId = InetAddress.getByName(router_id);
-		} catch (UnknownHostException e) {
-			System.out.println("InetAddress exception");
-		}
-		try {
-			this.nextHop = InetAddress.getByName(nexthop);
-		} catch (UnknownHostException e) {
-			System.out.println("InetAddress exception");
-		}
-		this.masklen = masklen;
-	}
-	
-	public InetAddress getNextHop() {
-	    return nextHop;
-	}
-	
-	public int getMasklen(){
-	    return masklen;
-	}
-	
-	public boolean equals(Rib r) {
-				
-		return this.routerId.equals(r.routerId) && this.nextHop.equals(r.nextHop)  && this.masklen == r.masklen;
-		
-	}
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java
new file mode 100644
index 0000000..1520c60
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java
@@ -0,0 +1,44 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.net.InetAddress;
+
+import com.google.common.net.InetAddresses;
+
+public class RibEntry {
+	private final InetAddress routerId;
+	private final InetAddress nextHop;
+	
+	public RibEntry(InetAddress routerId, InetAddress nextHop) {
+		this.routerId = routerId;
+		this.nextHop = nextHop;
+	}
+	
+	public RibEntry(String routerId, String nextHop) {
+		this.routerId = InetAddresses.forString(routerId);
+		this.nextHop = InetAddresses.forString(nextHop);
+	}
+	
+	public InetAddress getNextHop() {
+	    return nextHop;
+	}
+	
+	@Override
+	public boolean equals(Object other) {
+		if (other == null || !(other instanceof RibEntry)) {
+			return false;
+		}
+		
+		RibEntry otherRibEntry = (RibEntry) other;
+		
+		return this.routerId.equals(otherRibEntry.routerId) 
+				&& this.nextHop.equals(otherRibEntry.nextHop);
+	}
+	
+	@Override
+	public int hashCode() {
+		int hash = 17;
+		hash = 31 * hash + routerId.hashCode();
+		hash = 31 * hash + nextHop.hashCode();
+		return hash;
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibUpdate.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibUpdate.java
new file mode 100644
index 0000000..d866304
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibUpdate.java
@@ -0,0 +1,27 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+public class RibUpdate {
+	public enum Operation {UPDATE, DELETE}; 
+	
+	private final Operation operation;
+	private final Prefix prefix;
+	private final RibEntry ribEntry;
+	
+	public RibUpdate(Operation operation, Prefix prefix, RibEntry ribEntry) {
+		this.operation = operation;
+		this.prefix = prefix;
+		this.ribEntry = ribEntry;
+	}
+
+	public Operation getOperation() {
+		return operation;
+	}
+
+	public Prefix getPrefix() {
+		return prefix;
+	}
+
+	public RibEntry getRibEntry() {
+		return ribEntry;
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
index 04f9384..7b38fef 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
@@ -8,7 +8,6 @@
 
 import com.tinkerpop.blueprints.Direction;
 import com.tinkerpop.frames.Adjacency;
-import com.tinkerpop.frames.Incidence;
 import com.tinkerpop.frames.Property;
 import com.tinkerpop.frames.annotations.gremlin.GremlinGroovy;
 import com.tinkerpop.frames.annotations.gremlin.GremlinParam;
@@ -200,6 +199,13 @@
 		@Property("installer_id")
 		public void setInstallerId(String installerId);
 
+		@JsonProperty("flowPathFlags")
+		@Property("flow_path_flags")
+		public Long getFlowPathFlags();
+
+		@Property("flow_path_flags")
+		public void setFlowPathFlags(Long flowPathFlags);
+
 		@JsonProperty("srcDpid")
 		@Property("src_switch")
 		public String getSrcSwitch();
@@ -246,6 +252,9 @@
 		@Adjacency(label="flow", direction=Direction.IN)
 		public void removeFlowEntry(final IFlowEntry flowEntry);
 
+		//
+		// Matching fields
+		//
 		@JsonIgnore
 		@Property("matchSrcMac")
 		public String getMatchSrcMac();
@@ -323,6 +332,18 @@
 		@Property("matchDstTcpUdpPort")
 		public void setMatchDstTcpUdpPort(Short matchDstTcpUdpPort);
 
+		//
+		// Action-related fields
+		//
+		@Property("actions")
+		public String getActions();
+
+		@Property("actions")
+		public void setActions(String actionsStr);
+
+		//
+		// Other fields
+		//
 		@JsonIgnore
 		@GremlinGroovy("it.in('flow').out('switch')")
 		public Iterable<ISwitchObject> getSwitches();
@@ -376,6 +397,9 @@
 		@Property("error_state_code")
 		public void setErrorStateCode(String errorStateCode);
 
+		//
+		// Matching fields
+		//
 		@Property("matchInPort")
 		public Short getMatchInPort();
 
@@ -448,12 +472,24 @@
 		@Property("matchDstTcpUdpPort")
 		public void setMatchDstTcpUdpPort(Short matchDstTcpUdpPort);
 
-		@Property("actionOutput")
-		public Short getActionOutput();
+		//
+		// Action-related fields
+		//
+		@Property("actionOutputPort")
+		public Short getActionOutputPort();
 
-		@Property("actionOutput")
-		public void setActionOutput(Short actionOutput);
+		@Property("actionOutputPort")
+		public void setActionOutputPort(Short actionOutputPort);
 
+		@Property("actions")
+		public String getActions();
+
+		@Property("actions")
+		public void setActions(String actionsStr);
+
+		//
+		// Other fields
+		//
 		@Adjacency(label="flow")
 		public IFlowPath getFlow();
 
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyService.java b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyService.java
index 1992624..954515b 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyService.java
@@ -83,7 +83,7 @@
 	     * See the documentation for method @ref prepareShortestPathTopo()
 	     * for additional information and usage.
 	     *
-	     * @shortestPathTopo the Shortest Path info handler to release.
+	     * @param shortestPathTopo the Shortest Path info handler to release.
 	     */
 	    void dropShortestPathTopo(Map<Long, ?> shortestPathTopo);
 
@@ -95,7 +95,7 @@
 	     * See the documentation for method @ref prepareShortestPathTopo()
 	     * for additional information and usage.
 	     *
-	     * @paran shortestPathTopoHandler the Shortest Path info handler
+	     * @param shortestPathTopo 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.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/ISwitchStorage.java b/src/main/java/net/onrc/onos/ofcontroller/core/ISwitchStorage.java
index a4dd528..4fcebb2 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/ISwitchStorage.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/ISwitchStorage.java
@@ -1,8 +1,5 @@
 package net.onrc.onos.ofcontroller.core;
 
-import java.util.Collection;
-
-
 import net.floodlightcontroller.core.IOFSwitch;
 
 import org.openflow.protocol.OFPhysicalPort;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java b/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java
index 6e42771..e5f2f58 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImpl.java
@@ -15,10 +15,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.thinkaurelius.titan.core.TitanException;
-import com.tinkerpop.blueprints.Direction;
-import com.tinkerpop.blueprints.Edge;
 import com.tinkerpop.blueprints.Vertex;
-import com.tinkerpop.gremlin.java.GremlinPipeline;
 import com.tinkerpop.pipes.PipeFunction;
 import com.tinkerpop.pipes.transform.PathPipe;
 
@@ -142,7 +139,7 @@
 	
 	/**
 	 * Delete a record in the LinkStorage.
-	 * @param link Record to be deleted.
+	 * @param lt Record to be deleted.
 	 */
 	@Override
 	public void deleteLink(Link lt) {
@@ -207,7 +204,7 @@
     	IPortObject srcPort = dbop.searchPort(HexString.toHexString(dpid), port);
     	ISwitchObject srcSw = srcPort.getSwitch();
     	
-    	if(srcSw != null) {
+    	if(srcSw != null && srcPort != null) {
         	for(IPortObject dstPort : srcPort.getLinkedPorts()) {
         		ISwitchObject dstSw = dstPort.getSwitch();
         		Link link = new Link(HexString.toLong(srcSw.getDPID()),
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java b/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java
index 126efb3..b7c97f8 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java
@@ -167,7 +167,11 @@
             	IPortObject p = op.searchPort(dpid, port.getPortNumber());
             	log.info("SwitchStorage:addPort dpid:{} port:{}", dpid, port.getPortNumber());
             	if (p != null) {
-            		log.error("SwitchStorage:addPort dpid:{} port:{} exists", dpid, port.getPortNumber());
+            		log.error("SwitchStorage:addPort dpid:{} port:{} exists setting as ACTIVE", dpid, port.getPortNumber());
+            		p.setState("ACTIVE");
+            		p.setPortState(port.getState());
+            		p.setDesc(port.getName());
+            		op.commit();
             	} else {
             		p = op.newPort(dpid, port.getPortNumber());
             		p.setState("ACTIVE");
@@ -200,9 +204,8 @@
             if (sw != null) {
             	IPortObject p = op.searchPort(dpid, port);
                 if (p != null) {
-            		log.info("SwitchStorage:deletePort dpid:{} port:{} found and deleted", dpid, port);
-            		sw.removePort(p);
-            		op.removePort(p);
+            		log.info("SwitchStorage:deletePort dpid:{} port:{} found and set INACTIVE", dpid, port);
+            		p.setState("INACTIVE");
             		op.commit();
             	}
             }
@@ -224,6 +227,9 @@
         		IPortObject p = op.searchPort(dpid, port.getPortNumber());
         		if (p != null) {
             		log.error("SwitchStorage:addPort dpid:{} port:{} exists", dpid, port.getPortNumber());
+            		p.setState("ACTIVE");
+            		p.setPortState(port.getState());
+            		p.setDesc(port.getName());
             	} else {
             		p = op.newPort(dpid, port.getPortNumber());
             		p.setState("ACTIVE");
diff --git a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
index 8c83a7c..e0ac4e1 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
@@ -127,19 +127,22 @@
 	public void linkDiscoveryUpdate(LDUpdate update) {
 		// TODO Auto-generated method stub
 		Link lt = new Link(update.getSrc(),update.getSrcPort(),update.getDst(),update.getDstPort());
-		log.debug("{}:LinkDicoveryUpdate(): Updating Link {}",this.getClass(), lt);
-		switch (update.getOperation()) {
+		//log.debug("{}:LinkDicoveryUpdate(): Updating Link {}",this.getClass(), lt);
 		
+		switch (update.getOperation()) {
 			case LINK_REMOVED:
+				log.debug("LinkDiscoveryUpdate(): Removing link {}", lt);
 				linkStore.update(lt, DM_OPERATION.DELETE);
 				// TODO: Move network map link removal here
 				// reconcile paths here
 //				IPortObject srcPort = conn.utils().searchPort(conn, HexString.toHexString(update.getSrc()), update.getSrcPort());
 				break;
 			case LINK_UPDATED:
+				log.debug("LinkDiscoveryUpdate(): Updating link {}", lt);
 				linkStore.update(lt, DM_OPERATION.UPDATE);
 				break;
 			case LINK_ADDED:
+				log.debug("LinkDiscoveryUpdate(): Adding link {}", lt);
 				linkStore.update(lt, DM_OPERATION.INSERT);
 				break;
 					
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
index ebef1f4..ce9941e 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
@@ -42,12 +42,15 @@
 import net.onrc.onos.ofcontroller.util.Dpid;
 import net.onrc.onos.ofcontroller.util.FlowEntry;
 import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.*;
+import net.onrc.onos.ofcontroller.util.FlowEntryActions;
 import net.onrc.onos.ofcontroller.util.FlowEntryId;
 import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
 import net.onrc.onos.ofcontroller.util.FlowEntrySwitchState;
 import net.onrc.onos.ofcontroller.util.FlowEntryUserState;
 import net.onrc.onos.ofcontroller.util.FlowId;
 import net.onrc.onos.ofcontroller.util.FlowPath;
+import net.onrc.onos.ofcontroller.util.FlowPathFlags;
 import net.onrc.onos.ofcontroller.util.IPv4Net;
 import net.onrc.onos.ofcontroller.util.Port;
 import net.onrc.onos.ofcontroller.util.SwitchPort;
@@ -57,12 +60,13 @@
 import org.openflow.protocol.OFPacketOut;
 import org.openflow.protocol.OFPort;
 import org.openflow.protocol.OFType;
-import org.openflow.protocol.action.OFAction;
-import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.protocol.action.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-
+/**
+ * Flow Manager class for handling the network flows.
+ */
 public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
 
     protected GraphDBOperation op;
@@ -95,11 +99,6 @@
     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);
@@ -108,6 +107,10 @@
     private ScheduledExecutorService mapReaderScheduler;
     private ScheduledExecutorService shortestPathReconcileScheduler;
 
+    /**
+     * Periodic task for reading the Flow Entries and pushing changes
+     * into the switches.
+     */
     final Runnable mapReader = new Runnable() {
 	    public void run() {
 		try {
@@ -130,6 +133,10 @@
 		}
 		Map<Long, IOFSwitch> mySwitches =
 		    floodlightProvider.getSwitches();
+		if (mySwitches.isEmpty()) {
+			log.trace("No switches controlled");
+			return;
+		}
 		LinkedList<IFlowEntry> addFlowEntries =
 		    new LinkedList<IFlowEntry>();
 		LinkedList<IFlowEntry> deleteFlowEntries =
@@ -248,6 +255,10 @@
 	    }
 	};
 
+    /**
+     * Periodic task for reading the Flow Paths and recomputing the
+     * shortest paths.
+     */
     final Runnable shortestPathReconcile = new Runnable() {
 	    public void run() {
 		try {
@@ -270,6 +281,10 @@
 		}
 		Map<Long, IOFSwitch> mySwitches =
 		    floodlightProvider.getSwitches();
+		if (mySwitches.isEmpty()) {
+			log.trace("No switches controlled");
+			return;
+		}
 		LinkedList<IFlowPath> deleteFlows = new LinkedList<IFlowPath>();
 
 		boolean processed_measurement_flow = false;
@@ -330,9 +345,11 @@
 		    Short srcPortShort = flowPathObj.getSrcPort();
 		    String dstDpidStr = flowPathObj.getDstSwitch();
 		    Short dstPortShort = flowPathObj.getDstPort();
+		    Long flowPathFlagsLong = flowPathObj.getFlowPathFlags();
 		    if ((srcPortShort == null) ||
 			(dstDpidStr == null) ||
-			(dstPortShort == null)) {
+			(dstPortShort == null) ||
+			(flowPathFlagsLong == null)) {
 			continue;
 		    }
 
@@ -341,6 +358,7 @@
 		    Port dstPort = new Port(dstPortShort);
 		    SwitchPort srcSwitchPort = new SwitchPort(srcDpid, srcPort);
 		    SwitchPort dstSwitchPort = new SwitchPort(dstDpid, dstPort);
+		    FlowPathFlags flowPathFlags = new FlowPathFlags(flowPathFlagsLong);
 
 		    counterMyFlowPaths++;
 
@@ -363,6 +381,7 @@
 			dataPath.setSrcPort(srcSwitchPort);
 			dataPath.setDstPort(dstSwitchPort);
 		    }
+		    dataPath.applyFlowPathFlags(flowPathFlags);
 
 		    String newDataPathSummaryStr = dataPath.dataPathSummary();
 		    if (dataPathSummaryStr.equals(newDataPathSummaryStr))
@@ -405,27 +424,38 @@
 	    }
 	};
 
-    //final ScheduledFuture<?> mapReaderHandle =
-	//mapReaderScheduler.scheduleAtFixedRate(mapReader, 3, 3, TimeUnit.SECONDS);
 
-    //final ScheduledFuture<?> shortestPathReconcileHandle =
-	//shortestPathReconcileScheduler.scheduleAtFixedRate(shortestPathReconcile, 3, 3, TimeUnit.SECONDS);
-
+    /**
+     * Initialize the Flow Manager.
+     *
+     * @param conf the Graph Database configuration string.
+     */
     @Override
     public void init(String conf) {
     	op = new GraphDBOperation(conf);
 	topoRouteService = new TopoRouteService(conf);
     }
 
+    /**
+     * Shutdown the Flow Manager operation.
+     */
     public void finalize() {
     	close();
     }
 
+    /**
+     * Shutdown the Flow Manager operation.
+     */
     @Override
     public void close() {
     	op.close();
     }
 
+    /**
+     * Get the collection of offered module services.
+     *
+     * @return the collection of offered module services.
+     */
     @Override
     public Collection<Class<? extends IFloodlightService>> getModuleServices() {
         Collection<Class<? extends IFloodlightService>> l = 
@@ -434,6 +464,11 @@
         return l;
     }
 
+    /**
+     * Get the collection of implemented services.
+     *
+     * @return the collection of implemented services.
+     */
     @Override
     public Map<Class<? extends IFloodlightService>, IFloodlightService> 
 			       getServiceImpls() {
@@ -445,6 +480,11 @@
         return m;
     }
 
+    /**
+     * Get the collection of modules this module depends on.
+     *
+     * @return the collection of modules this module depends on.
+     */
     @Override
     public Collection<Class<? extends IFloodlightService>> 
                                                     getModuleDependencies() {
@@ -455,6 +495,11 @@
         return l;
     }
 
+    /**
+     * Initialize the module.
+     *
+     * @param context the module context to use for the initialization.
+     */
     @Override
     public void init(FloodlightModuleContext context)
 	throws FloodlightModuleException {
@@ -469,10 +514,15 @@
 	String conf = "/tmp/cassandra.titan";
 	this.init(conf);
 	
-		mapReaderScheduler = Executors.newScheduledThreadPool(1);
-		shortestPathReconcileScheduler = Executors.newScheduledThreadPool(1);
+	mapReaderScheduler = Executors.newScheduledThreadPool(1);
+	shortestPathReconcileScheduler = Executors.newScheduledThreadPool(1);
     }
 
+    /**
+     * Get the next Flow Entry ID to use.
+     *
+     * @return the next Flow Entry ID to use.
+     */
     private synchronized long getNextFlowEntryId() {
 	//
 	// Generate the next Flow Entry ID.
@@ -491,17 +541,22 @@
 	return result;
     }
 
+    /**
+     * Startup module operation.
+     *
+     * @param context the module context to use for the startup.
+     */
     @Override
     public void startUp(FloodlightModuleContext context) {
-		restApi.addRestletRoutable(new FlowWebRoutable());
+	restApi.addRestletRoutable(new FlowWebRoutable());
 	
-		// Initialize the Flow Entry ID generator
-		nextFlowEntryIdPrefix = randomGenerator.nextInt();
+	// Initialize the Flow Entry ID generator
+	nextFlowEntryIdPrefix = randomGenerator.nextInt();
 		
-		mapReaderScheduler.scheduleAtFixedRate(
-				mapReader, 3, 3, TimeUnit.SECONDS);
-		shortestPathReconcileScheduler.scheduleAtFixedRate(
-				shortestPathReconcile, 3, 3, TimeUnit.SECONDS);
+	mapReaderScheduler.scheduleAtFixedRate(
+			mapReader, 3, 3, TimeUnit.SECONDS);
+	shortestPathReconcileScheduler.scheduleAtFixedRate(
+			shortestPathReconcile, 3, 3, TimeUnit.SECONDS);
     }
 
     /**
@@ -568,6 +623,7 @@
 	//
 	// Set the Flow attributes:
 	// - flowPath.installerId()
+	// - flowPath.flowPathFlags()
 	// - flowPath.dataPath().srcPort()
 	// - flowPath.dataPath().dstPort()
 	// - flowPath.matchSrcMac()
@@ -581,8 +637,10 @@
 	// - flowPath.matchIpToS()
 	// - flowPath.matchSrcTcpUdpPort()
 	// - flowPath.matchDstTcpUdpPort()
+	// - flowPath.flowEntryActions()
 	//
 	flowObj.setInstallerId(flowPath.installerId().toString());
+	flowObj.setFlowPathFlags(flowPath.flowPathFlags().flags());
 	flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
 	flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
 	flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
@@ -620,6 +678,9 @@
 	if (flowPath.flowEntryMatch().matchDstTcpUdpPort()) {
 	    flowObj.setMatchDstTcpUdpPort(flowPath.flowEntryMatch().dstTcpUdpPort());
 	}
+	if (! flowPath.flowEntryActions().actions().isEmpty()) {
+	    flowObj.setActions(flowPath.flowEntryActions().toString());
+	}
 
 	if (dataPathSummaryStr != null) {
 	    flowObj.setDataPathSummary(dataPathSummaryStr);
@@ -713,8 +774,6 @@
 	// - InPort edge
 	// - OutPort edge
 	//
-	// - flowEntry.flowEntryMatch()
-	// - flowEntry.flowEntryActions()
 	// - flowEntry.dpid()
 	// - flowEntry.flowEntryUserState()
 	// - flowEntry.flowEntrySwitchState()
@@ -731,10 +790,10 @@
 	// - flowEntry.matchIpToS()
 	// - flowEntry.matchSrcTcpUdpPort()
 	// - flowEntry.matchDstTcpUdpPort()
-	// - flowEntry.actionOutput()
+	// - flowEntry.actionOutputPort()
+	// - flowEntry.actions()
 	//
-	ISwitchObject sw =
-	    op.searchSwitch(flowEntry.dpid().toString());
+	ISwitchObject sw = op.searchSwitch(flowEntry.dpid().toString());
 	flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
 	flowEntryObj.setSwitch(sw);
 	if (flowEntry.flowEntryMatch().matchInPort()) {
@@ -778,15 +837,19 @@
 	    flowEntryObj.setMatchDstTcpUdpPort(flowEntry.flowEntryMatch().dstTcpUdpPort());
 	}
 
-	for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
+	for (FlowEntryAction fa : flowEntry.flowEntryActions().actions()) {
 	    if (fa.actionOutput() != null) {
 		IPortObject outport =
 		    op.searchPort(flowEntry.dpid().toString(),
 					      fa.actionOutput().port().value());
-		flowEntryObj.setActionOutput(fa.actionOutput().port().value());
+		flowEntryObj.setActionOutputPort(fa.actionOutput().port().value());
 		flowEntryObj.setOutPort(outport);
 	    }
 	}
+	if (! flowEntry.flowEntryActions().isEmpty()) {
+	    flowEntryObj.setActions(flowEntry.flowEntryActions().toString());
+	}
+
 	// TODO: Hacks with hard-coded state names!
 	if (found)
 	    flowEntryObj.setUserState("FE_USER_MODIFY");
@@ -1146,21 +1209,22 @@
     }
 
     /**
-     * Get summary of all installed flows by all installers in a given range
+     * Get summary of all installed flows by all installers in a given range.
      *
-     * @param flowId the data path endpoints of the flows to get.
-     * @param maxFlows: the maximum number of flows to be returned
+     * @param flowId the Flow ID of the first flow in the flow range to get.
+     * @param maxFlows the maximum number of flows to be returned.
      * @return the Flow Paths if found, otherwise null.
      */
     @Override
     public ArrayList<IFlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows) {
 
-		// TODO: The implementation below is not optimal:
-		// We fetch all flows, and then return only the subset that match
-		// the query conditions.
-		// We should use the appropriate Titan/Gremlin query to filter-out
-		// the flows as appropriate.
-		//
+	//
+	// TODO: The implementation below is not optimal:
+	// We fetch all flows, and then return only the subset that match
+	// the query conditions.
+	// We should use the appropriate Titan/Gremlin query to filter-out
+	// the flows as appropriate.
+	//
     	//ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
     	
     	ArrayList<IFlowPath> flowPathsWithoutFlowEntries = getAllFlowsWithoutFlowEntries();
@@ -1260,8 +1324,13 @@
 
 	return flowPaths;
     }
-    
-    public ArrayList<IFlowPath> getAllFlowsWithoutFlowEntries(){
+
+    /**
+     * Get all Flows information, without the associated Flow Entries.
+     *
+     * @return all Flows information, without the associated Flow Entries.
+     */
+    public ArrayList<IFlowPath> getAllFlowsWithoutFlowEntries() {
     	Iterable<IFlowPath> flowPathsObj = null;
     	ArrayList<IFlowPath> flowPathsObjArray = new ArrayList<IFlowPath>();
     	ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
@@ -1314,6 +1383,7 @@
 	//
 	String flowIdStr = flowObj.getFlowId();
 	String installerIdStr = flowObj.getInstallerId();
+	Long flowPathFlags = flowObj.getFlowPathFlags();
 	String srcSwitchStr = flowObj.getSrcSwitch();
 	Short srcPortShort = flowObj.getSrcPort();
 	String dstSwitchStr = flowObj.getDstSwitch();
@@ -1321,6 +1391,7 @@
 
 	if ((flowIdStr == null) ||
 	    (installerIdStr == null) ||
+	    (flowPathFlags == null) ||
 	    (srcSwitchStr == null) ||
 	    (srcPortShort == null) ||
 	    (dstSwitchStr == null) ||
@@ -1332,6 +1403,7 @@
 	FlowPath flowPath = new FlowPath();
 	flowPath.setFlowId(new FlowId(flowIdStr));
 	flowPath.setInstallerId(new CallerId(installerIdStr));
+	flowPath.setFlowPathFlags(new FlowPathFlags(flowPathFlags));
 	flowPath.dataPath().srcPort().setDpid(new Dpid(srcSwitchStr));
 	flowPath.dataPath().srcPort().setPort(new Port(srcPortShort));
 	flowPath.dataPath().dstPort().setDpid(new Dpid(dstSwitchStr));
@@ -1377,6 +1449,16 @@
 
 	    flowPath.setFlowEntryMatch(match);
 	}
+	//
+	// Extract the actions for the first Flow Entry
+	//
+	{
+	    String actionsStr = flowObj.getActions();
+	    if (actionsStr != null) {
+		FlowEntryActions flowEntryActions = new FlowEntryActions(actionsStr);
+		flowPath.setFlowEntryActions(flowEntryActions);
+	    }
+	}
 
 	//
 	// Extract all Flow Entries
@@ -1461,19 +1543,15 @@
 	//
 	// Extract the actions
 	//
-	ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
-	Short actionOutputPort = flowEntryObj.getActionOutput();
-	if (actionOutputPort != null) {
-	    FlowEntryAction action = new FlowEntryAction();
-	    action.setActionOutput(new Port(actionOutputPort));
-	    actions.add(action);
-	}
+	FlowEntryActions actions = new FlowEntryActions();
+	String actionsStr = flowEntryObj.getActions();
+	if (actionsStr != null)
+	    actions = new FlowEntryActions(actionsStr);
 	flowEntry.setFlowEntryActions(actions);
 	flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
 	flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
 	//
-	// TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
-	// and FlowEntryErrorState.
+	// TODO: Take care of FlowEntryErrorState.
 	//
 	return flowEntry;
     }
@@ -1505,8 +1583,10 @@
 	FlowPath computedFlowPath = new FlowPath();
 	computedFlowPath.setFlowId(new FlowId(flowPath.flowId().value()));
 	computedFlowPath.setInstallerId(new CallerId(flowPath.installerId().value()));
+	computedFlowPath.setFlowPathFlags(new FlowPathFlags(flowPath.flowPathFlags().flags()));
 	computedFlowPath.setDataPath(dataPath);
 	computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
+	computedFlowPath.setFlowEntryActions(new FlowEntryActions(flowPath.flowEntryActions()));
 
 	FlowId flowId = new FlowId();
 	String dataPathSummaryStr = dataPath.dataPathSummary();
@@ -1532,21 +1612,35 @@
 	// Set the incoming port matching and the outgoing port output
 	// actions for each flow entry.
 	//
+	int idx = 0;
 	for (FlowEntry flowEntry : newDataPath.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);
+	    //
+	    // Set the actions
+	    //
+	    FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
+	    //
+	    // If the first Flow Entry, copy the Flow Path actions to it
+	    //
+	    if (idx == 0) {
+		String actionsStr = flowObj.getActions();
+		if (actionsStr != null) {
+		    FlowEntryActions flowActions = new FlowEntryActions(actionsStr);
+		    for (FlowEntryAction action : flowActions.actions())
+			flowEntryActions.addAction(action);
+		}
 	    }
+	    idx++;
+	    //
+	    // Add the outgoing port output action
+	    //
 	    FlowEntryAction flowEntryAction = new FlowEntryAction();
 	    flowEntryAction.setActionOutput(flowEntry.outPort());
-	    flowEntryActions.add(flowEntryAction);
+	    flowEntryActions.addAction(flowEntryAction);
 	}
 
 	//
@@ -1738,16 +1832,115 @@
 	//
 	// Fetch the actions
 	//
-	// TODO: For now we support only the "OUTPUT" actions.
-	//
-	List<OFAction> actions = new ArrayList<OFAction>();
-	Short actionOutputPort = flowEntryObj.getActionOutput();
-	if (actionOutputPort != null) {
-	    OFActionOutput action = new OFActionOutput();
-	    // XXX: The max length is hard-coded for now
-	    action.setMaxLength((short)0xffff);
-	    action.setPort(actionOutputPort);
-	    actions.add(action);
+	Short actionOutputPort = null;
+	List<OFAction> openFlowActions = new ArrayList<OFAction>();
+	int actionsLen = 0;
+	FlowEntryActions flowEntryActions = null;
+	String actionsStr = flowEntryObj.getActions();
+	if (actionsStr != null)
+	    flowEntryActions = new FlowEntryActions(actionsStr);
+	for (FlowEntryAction action : flowEntryActions.actions()) {
+	    ActionOutput actionOutput = action.actionOutput();
+	    ActionSetVlanId actionSetVlanId = action.actionSetVlanId();
+	    ActionSetVlanPriority actionSetVlanPriority = action.actionSetVlanPriority();
+	    ActionStripVlan actionStripVlan = action.actionStripVlan();
+	    ActionSetEthernetAddr actionSetEthernetSrcAddr = action.actionSetEthernetSrcAddr();
+	    ActionSetEthernetAddr actionSetEthernetDstAddr = action.actionSetEthernetDstAddr();
+	    ActionSetIPv4Addr actionSetIPv4SrcAddr = action.actionSetIPv4SrcAddr();
+	    ActionSetIPv4Addr actionSetIPv4DstAddr = action.actionSetIPv4DstAddr();
+	    ActionSetIpToS actionSetIpToS = action.actionSetIpToS();
+	    ActionSetTcpUdpPort actionSetTcpUdpSrcPort = action.actionSetTcpUdpSrcPort();
+	    ActionSetTcpUdpPort actionSetTcpUdpDstPort = action.actionSetTcpUdpDstPort();
+	    ActionEnqueue actionEnqueue = action.actionEnqueue();
+
+	    if (actionOutput != null) {
+		actionOutputPort = actionOutput.port().value();
+		// XXX: The max length is hard-coded for now
+		OFActionOutput ofa =
+		    new OFActionOutput(actionOutput.port().value(),
+				       (short)0xffff);
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetVlanId != null) {
+		OFActionVirtualLanIdentifier ofa =
+		    new OFActionVirtualLanIdentifier(actionSetVlanId.vlanId());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetVlanPriority != null) {
+		OFActionVirtualLanPriorityCodePoint ofa =
+		    new OFActionVirtualLanPriorityCodePoint(actionSetVlanPriority.vlanPriority());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionStripVlan != null) {
+		if (actionStripVlan.stripVlan() == true) {
+		    OFActionStripVirtualLan ofa = new OFActionStripVirtualLan();
+		    openFlowActions.add(ofa);
+		    actionsLen += ofa.getLength();
+		}
+	    }
+
+	    if (actionSetEthernetSrcAddr != null) {
+		OFActionDataLayerSource ofa = 
+		    new OFActionDataLayerSource(actionSetEthernetSrcAddr.addr().toBytes());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetEthernetDstAddr != null) {
+		OFActionDataLayerDestination ofa =
+		    new OFActionDataLayerDestination(actionSetEthernetDstAddr.addr().toBytes());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIPv4SrcAddr != null) {
+		OFActionNetworkLayerSource ofa =
+		    new OFActionNetworkLayerSource(actionSetIPv4SrcAddr.addr().value());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIPv4DstAddr != null) {
+		OFActionNetworkLayerDestination ofa =
+		    new OFActionNetworkLayerDestination(actionSetIPv4DstAddr.addr().value());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIpToS != null) {
+		OFActionNetworkTypeOfService ofa =
+		    new OFActionNetworkTypeOfService(actionSetIpToS.ipToS());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetTcpUdpSrcPort != null) {
+		OFActionTransportLayerSource ofa =
+		    new OFActionTransportLayerSource(actionSetTcpUdpSrcPort.port());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetTcpUdpDstPort != null) {
+		OFActionTransportLayerDestination ofa =
+		    new OFActionTransportLayerDestination(actionSetTcpUdpDstPort.port());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionEnqueue != null) {
+		OFActionEnqueue ofa =
+		    new OFActionEnqueue(actionEnqueue.port().value(),
+					actionEnqueue.queueId());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
 	}
 
 	fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
@@ -1757,8 +1950,8 @@
 	    .setCookie(cookie)
 	    .setCommand(flowModCommand)
 	    .setMatch(match)
-	    .setActions(actions)
-	    .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+	    .setActions(openFlowActions)
+	    .setLengthU(OFFlowMod.MINIMUM_LENGTH + actionsLen);
 	fm.setOutPort(OFPort.OFPP_NONE.getValue());
 	if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
 	    (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
@@ -1962,27 +2155,113 @@
 	//
 	// Fetch the actions
 	//
-	// TODO: For now we support only the "OUTPUT" actions.
+	Short actionOutputPort = null;
+	List<OFAction> openFlowActions = new ArrayList<OFAction>();
+	int actionsLen = 0;
+	FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
 	//
-	fm.setOutPort(OFPort.OFPP_NONE.getValue());
-	List<OFAction> actions = new ArrayList<OFAction>();
-	ArrayList<FlowEntryAction> flowEntryActions =
-	    flowEntry.flowEntryActions();
-	for (FlowEntryAction flowEntryAction : flowEntryActions) {
-	    FlowEntryAction.ActionOutput actionOutput =
-		flowEntryAction.actionOutput();
+	for (FlowEntryAction action : flowEntryActions.actions()) {
+	    ActionOutput actionOutput = action.actionOutput();
+	    ActionSetVlanId actionSetVlanId = action.actionSetVlanId();
+	    ActionSetVlanPriority actionSetVlanPriority = action.actionSetVlanPriority();
+	    ActionStripVlan actionStripVlan = action.actionStripVlan();
+	    ActionSetEthernetAddr actionSetEthernetSrcAddr = action.actionSetEthernetSrcAddr();
+	    ActionSetEthernetAddr actionSetEthernetDstAddr = action.actionSetEthernetDstAddr();
+	    ActionSetIPv4Addr actionSetIPv4SrcAddr = action.actionSetIPv4SrcAddr();
+	    ActionSetIPv4Addr actionSetIPv4DstAddr = action.actionSetIPv4DstAddr();
+	    ActionSetIpToS actionSetIpToS = action.actionSetIpToS();
+	    ActionSetTcpUdpPort actionSetTcpUdpSrcPort = action.actionSetTcpUdpSrcPort();
+	    ActionSetTcpUdpPort actionSetTcpUdpDstPort = action.actionSetTcpUdpDstPort();
+	    ActionEnqueue actionEnqueue = action.actionEnqueue();
+
 	    if (actionOutput != null) {
-		short actionOutputPort = actionOutput.port().value();
-		OFActionOutput action = new OFActionOutput();
+		actionOutputPort = actionOutput.port().value();
 		// XXX: The max length is hard-coded for now
-		action.setMaxLength((short)0xffff);
-		action.setPort(actionOutputPort);
-		actions.add(action);
-		if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
-		    (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
-		    fm.setOutPort(actionOutputPort);
+		OFActionOutput ofa =
+		    new OFActionOutput(actionOutput.port().value(),
+				       (short)0xffff);
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetVlanId != null) {
+		OFActionVirtualLanIdentifier ofa =
+		    new OFActionVirtualLanIdentifier(actionSetVlanId.vlanId());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetVlanPriority != null) {
+		OFActionVirtualLanPriorityCodePoint ofa =
+		    new OFActionVirtualLanPriorityCodePoint(actionSetVlanPriority.vlanPriority());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionStripVlan != null) {
+		if (actionStripVlan.stripVlan() == true) {
+		    OFActionStripVirtualLan ofa = new OFActionStripVirtualLan();
+		    openFlowActions.add(ofa);
+		    actionsLen += ofa.getLength();
 		}
 	    }
+
+	    if (actionSetEthernetSrcAddr != null) {
+		OFActionDataLayerSource ofa = 
+		    new OFActionDataLayerSource(actionSetEthernetSrcAddr.addr().toBytes());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetEthernetDstAddr != null) {
+		OFActionDataLayerDestination ofa =
+		    new OFActionDataLayerDestination(actionSetEthernetDstAddr.addr().toBytes());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIPv4SrcAddr != null) {
+		OFActionNetworkLayerSource ofa =
+		    new OFActionNetworkLayerSource(actionSetIPv4SrcAddr.addr().value());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIPv4DstAddr != null) {
+		OFActionNetworkLayerDestination ofa =
+		    new OFActionNetworkLayerDestination(actionSetIPv4DstAddr.addr().value());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIpToS != null) {
+		OFActionNetworkTypeOfService ofa =
+		    new OFActionNetworkTypeOfService(actionSetIpToS.ipToS());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetTcpUdpSrcPort != null) {
+		OFActionTransportLayerSource ofa =
+		    new OFActionTransportLayerSource(actionSetTcpUdpSrcPort.port());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetTcpUdpDstPort != null) {
+		OFActionTransportLayerDestination ofa =
+		    new OFActionTransportLayerDestination(actionSetTcpUdpDstPort.port());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionEnqueue != null) {
+		OFActionEnqueue ofa =
+		    new OFActionEnqueue(actionEnqueue.port().value(),
+					actionEnqueue.queueId());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
 	}
 
 	fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
@@ -1992,8 +2271,14 @@
 	    .setCookie(cookie)
 	    .setCommand(flowModCommand)
 	    .setMatch(match)
-	    .setActions(actions)
-	    .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+	    .setActions(openFlowActions)
+	    .setLengthU(OFFlowMod.MINIMUM_LENGTH + actionsLen);
+	fm.setOutPort(OFPort.OFPP_NONE.getValue());
+	if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
+	    (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
+	    if (actionOutputPort != null)
+		fm.setOutPort(actionOutputPort);
+	}
 
 	//
 	// TODO: Set the following flag
@@ -2074,209 +2359,4 @@
 	//
 	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/onrc/onos/ofcontroller/flowmanager/IFlowService.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
index ba9cd1b..df11d6b 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/IFlowService.java
@@ -10,7 +10,7 @@
 import net.onrc.onos.ofcontroller.util.FlowPath;
 
 /**
- * @short Interface for providing Flow Service to other modules.
+ * Interface for providing Flow Service to other modules.
  */
 public interface IFlowService extends IFloodlightService {
     /**
@@ -88,8 +88,8 @@
     /**
      * Get summary of all installed flows by all installers.
      *
-     * @param flowId: starting flow Id of the range
-     * @param maxFlows: number of flows to return
+     * @param flowId starting flow Id of the range
+     * @param maxFlows number of flows to return
      * @return the Flow Paths if found, otherwise null.
      */
     ArrayList<IFlowPath> getAllFlowsSummary(FlowId flowId, int maxFlows);
@@ -115,47 +115,4 @@
      * @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/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java
index 2800305..7464ec5 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddFlowResource.java
@@ -14,10 +14,23 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Flow Manager REST API implementation: Add a Flow with the Flow
+ * Entries:
+ *
+ *   POST /wm/flow/add/json
+ */
 public class AddFlowResource extends ServerResource {
 
     protected static Logger log = LoggerFactory.getLogger(AddFlowResource.class);
 
+    /**
+     * Implement the API.
+     *
+     * @param flowJson a string with the JSON representation of the Flow to
+     * add.
+     * @return the Flow ID of the added flow.
+     */
     @Post("json")
     public FlowId store(String flowJson) {
 	FlowId result = new FlowId();
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddShortestPathFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddShortestPathFlowResource.java
index 9d2e0c9..c454b0f 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddShortestPathFlowResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/AddShortestPathFlowResource.java
@@ -14,10 +14,23 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Flow Manager REST API implementation: Add a Flow by delegating
+ * the Shortest Path computation to ONOS:
+ *
+ *   POST /wm/flow/add-shortest-path/json
+ */
 public class AddShortestPathFlowResource extends ServerResource {
 
     protected static Logger log = LoggerFactory.getLogger(AddShortestPathFlowResource.class);
 
+    /**
+     * Implement the API.
+     *
+     * @param flowJson a string with the JSON representation of the Flow to
+     * add.
+     * @return the Flow ID of the added flow.
+     */
     @Post("json")
     public FlowId store(String flowJson) {
 	FlowId result = new FlowId();
@@ -53,10 +66,11 @@
 	if (flowPath != null) {
 	    FlowPath addedFlowPath =
 		flowService.addAndMaintainShortestPathFlow(flowPath);
-	    if (addedFlowPath == null)
+	    if (addedFlowPath == null) {
 		result = new FlowId();		// Error: Return empty Flow Id
-	    else
+	    } else {
 		result = addedFlowPath.flowId();
+	    }
 	}
 
         return result;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/ClearFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/ClearFlowResource.java
index 1daa2ab..db591ae 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/ClearFlowResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/ClearFlowResource.java
@@ -8,9 +8,22 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Flow Manager REST API implementation: Clear internal Flow state.
+ *
+ * The "{flow-id}" request attribute value can be either a specific Flow ID,
+ * or the keyword "all" to clear all Flows:
+ *
+ *   GET /wm/flow/clear/{flow-id}/json
+ */
 public class ClearFlowResource extends ServerResource {
     protected static Logger log = LoggerFactory.getLogger(ClearFlowResource.class);
 
+    /**
+     * Implement the API.
+     *
+     * @return true on success, otehrwise false.
+     */
     @Get("json")
     public Boolean retrieve() {
 	Boolean result = false;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/DeleteFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/DeleteFlowResource.java
index 393ff44..5b2bad4 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/DeleteFlowResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/DeleteFlowResource.java
@@ -8,9 +8,22 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Flow Manager REST API implementation: Delete Flow state.
+ *
+ * The "{flow-id}" request attribute value can be either a specific Flow ID,
+ * or the keyword "all" to delete all Flows:
+ *
+ *   GET /wm/flow/delete/{flow-id}/json
+ */
 public class DeleteFlowResource extends ServerResource {
     protected static Logger log = LoggerFactory.getLogger(DeleteFlowResource.class);
 
+    /**
+     * Implement the API.
+     *
+     * @return true on success, otehrwise false.
+     */
     @Get("json")
     public Boolean retrieve() {
 	Boolean result = false;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/FlowWebRoutable.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/FlowWebRoutable.java
index 954c84d..e027270 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/FlowWebRoutable.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/FlowWebRoutable.java
@@ -6,6 +6,9 @@
 import org.restlet.Restlet;
 import org.restlet.routing.Router;
 
+/**
+ * REST API implementation for the Flow Manager.
+ */
 public class FlowWebRoutable implements RestletRoutable {
     /**
      * Create the Restlet router and bind to the proper resources.
@@ -22,11 +25,6 @@
         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/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsByEndpointsResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsByEndpointsResource.java
index 6142096..0308e89 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsByEndpointsResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsByEndpointsResource.java
@@ -14,9 +14,29 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Flow Manager REST API implementation: Get all Flow state for given
+ * source and destination switches and ports.
+ *
+ * The "{src-dpid}" request attribute value is the source DPID of the flows to
+ * get.
+ * The "{src-port}" request attribute value is the source port of the flows to
+ * get.
+ * The "{dst-dpid}" request attribute value is the destination DPID of the
+ * flows to get.
+ * The "{dst-port}" request attribute value is the destination port of the
+ * flows to get.
+ *
+ *   GET /wm/flow/getall-by-endpoints/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json"
+ */
 public class GetAllFlowsByEndpointsResource extends ServerResource {
     protected static Logger log = LoggerFactory.getLogger(GetAllFlowsByEndpointsResource.class);
 
+    /**
+     * Implement the API.
+     *
+     * @return the collection of Flow states if any found, otherwise null.
+     */
     @Get("json")
     public ArrayList<FlowPath> retrieve() {
 	ArrayList<FlowPath> result = null;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsByInstallerIdResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsByInstallerIdResource.java
index 498108f..43f1618 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsByInstallerIdResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsByInstallerIdResource.java
@@ -15,9 +15,31 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Flow Manager REST API implementation: Get all Flow state for a given
+ * Installer ID and given source and destination switches and ports.
+ *
+ * The "{installer-id}" request attribute value is the Installer ID of the
+ * flows to get.
+ * The "{src-dpid}" request attribute value is the source DPID of the flows to
+ * get.
+ * The "{src-port}" request attribute value is the source port of the flows to
+ * get.
+ * The "{dst-dpid}" request attribute value is the destination DPID of the
+ * flows to get.
+ * The "{dst-port}" request attribute value is the destination port of the
+ * flows to get.
+ *
+ *   GET /wm/flow/getall-by-installer-id/{installer-id}/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json"
+ */
 public class GetAllFlowsByInstallerIdResource extends ServerResource {
     protected static Logger log = LoggerFactory.getLogger(GetAllFlowsByInstallerIdResource.class);
 
+    /**
+     * Implement the API.
+     *
+     * @return the collection of Flow states if any found, otherwise null.
+     */
     @Get("json")
     public ArrayList<FlowPath> retrieve() {
 	ArrayList<FlowPath> result = null;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsResource.java
index 61eaf27..ab88348 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetAllFlowsResource.java
@@ -10,9 +10,19 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Flow Manager REST API implementation: Get all Flow state.
+ *
+ *   GET /wm/flow/getall/json"
+ */
 public class GetAllFlowsResource extends ServerResource {
     protected static Logger log = LoggerFactory.getLogger(GetAllFlowsResource.class);
 
+    /**
+     * Implement the API.
+     *
+     * @return the collection of Flow states if any found, otherwise null.
+     */
     @Get("json")
     public ArrayList<FlowPath> retrieve() {
 	ArrayList<FlowPath> result = null;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetFlowByIdResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetFlowByIdResource.java
index 48e7369..77a898c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetFlowByIdResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetFlowByIdResource.java
@@ -9,9 +9,21 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Flow Manager REST API implementation: Get a single Flow state.
+ *
+ * The "{flow-id}" request attribute value is the Flow ID of the flow to get:
+ *
+ *   GET /wm/flow/get/{flow-id}/json
+ */
 public class GetFlowByIdResource extends ServerResource {
     protected static Logger log = LoggerFactory.getLogger(GetFlowByIdResource.class);
 
+    /**
+     * Implement the API.
+     *
+     * @return the Flow state if the flow is found, otherwise null.
+     */
     @Get("json")
     public FlowPath retrieve() {
 	FlowPath result = null;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetSummaryFlowsResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetSummaryFlowsResource.java
index 4b3c00f..67e1ad6 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetSummaryFlowsResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/GetSummaryFlowsResource.java
@@ -11,9 +11,25 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Flow Manager REST API implementation: Get summary of all installed
+ * flows by all installers in a given range.
+ *
+ * The "{flow-id}" request attribute value is the Flow ID of the flow in the
+ * flow range to get.
+ * The "{max-flows}" request attribute value is the maximum number of flows
+ * to be returned.
+ *
+ *   GET /wm/flow/getsummary/{flow-id}/{max-flows}/json"
+ */
 public class GetSummaryFlowsResource extends ServerResource {
     protected static Logger log = LoggerFactory.getLogger(GetSummaryFlowsResource.class);
 
+    /**
+     * Implement the API.
+     *
+     * @return the collection of Flow states if any found, otherwise null.
+     */
     @Get("json")
     public ArrayList<IFlowPath> retrieve() {
     	ArrayList<IFlowPath> result = null;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementClearAllPathsFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementClearAllPathsFlowResource.java
deleted file mode 100644
index 07d9fb2..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementClearAllPathsFlowResource.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager.web;
-
-import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
-
-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/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetInstallPathsTimeNsecFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetInstallPathsTimeNsecFlowResource.java
deleted file mode 100644
index 467afca..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetInstallPathsTimeNsecFlowResource.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager.web;
-
-import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
-
-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/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetPerFlowInstallTimeFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetPerFlowInstallTimeFlowResource.java
deleted file mode 100644
index 92d84ab..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementGetPerFlowInstallTimeFlowResource.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager.web;
-
-import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
-
-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/onrc/onos/ofcontroller/flowmanager/web/MeasurementInstallPathsFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementInstallPathsFlowResource.java
deleted file mode 100644
index 074dfb4..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementInstallPathsFlowResource.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager.web;
-
-import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
-
-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/onrc/onos/ofcontroller/flowmanager/web/MeasurementStorePathFlowResource.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementStorePathFlowResource.java
deleted file mode 100644
index 0f23663..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/web/MeasurementStorePathFlowResource.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package net.onrc.onos.ofcontroller.flowmanager.web;
-
-import java.io.IOException;
-
-import net.onrc.onos.ofcontroller.flowmanager.IFlowService;
-import net.onrc.onos.ofcontroller.util.FlowId;
-import net.onrc.onos.ofcontroller.util.FlowPath;
-
-import org.codehaus.jackson.JsonGenerationException;
-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/onrc/onos/ofcontroller/linkdiscovery/ILinkDiscovery.java b/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/ILinkDiscovery.java
index 6113ea8..cdb71be 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/ILinkDiscovery.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/ILinkDiscovery.java
@@ -1,5 +1,7 @@
 package net.onrc.onos.ofcontroller.linkdiscovery;
 
+import net.floodlightcontroller.core.IUpdate;
+
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 import org.codehaus.jackson.map.ser.std.ToStringSerializer;
 import org.openflow.util.HexString;
@@ -27,7 +29,7 @@
         }
     }
 
-    public class LDUpdate {
+    public class LDUpdate implements IUpdate{
         protected long src;
         protected short srcPort;
         protected long dst;
@@ -129,6 +131,12 @@
                 return "LDUpdate: Unknown update.";
             }
         }
+
+		@Override
+		public void dispatch() {
+			// TODO Auto-generated method stub
+			
+		}
     }
 
     public enum SwitchType {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index f057a29..c9f3d5e 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -18,6 +18,7 @@
 package net.onrc.onos.ofcontroller.linkdiscovery.internal;
 
 import java.io.IOException;
+
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.NetworkInterface;
@@ -33,7 +34,6 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -126,6 +126,7 @@
 implements IOFMessageListener, IOFSwitchListener, 
 IStorageSourceListener, ILinkDiscoveryService,
 IFloodlightModule, IInfoProvider, IHAListener {
+	protected IFloodlightProviderService controller;
     protected static Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class);
 
     // Names of table/fields for links in the storage API
@@ -157,8 +158,8 @@
     private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
 
     // BigSwitch OUI is 5C:16:C7, so 5D:16:C7 is the multicast version
-    private static final String LLDP_BSN_DST_MAC_STRING = "5d:16:c7:00:00:01";
-    //private static final String LLDP_BSN_DST_MAC_STRING = "ff:ff:ff:ff:ff:ff";
+    // private static final String LLDP_BSN_DST_MAC_STRING = "5d:16:c7:00:00:01";
+    private static final String LLDP_BSN_DST_MAC_STRING = "ff:ff:ff:ff:ff:ff";
 
 
     // Direction TLVs are used to indicate if the LLDPs were sent 
@@ -232,8 +233,40 @@
     /* topology aware components are called in the order they were added to the
      * the array */
     protected ArrayList<ILinkDiscoveryListener> linkDiscoveryAware;
-    protected BlockingQueue<LDUpdate> updates;
-    protected Thread updatesThread;
+    
+    protected class LinkUpdate extends LDUpdate {
+
+		public LinkUpdate(LDUpdate old) {
+			super(old);
+		}
+		@LogMessageDoc(level="ERROR",
+	            message="Error in link discovery updates loop",
+	            explanation="An unknown error occured while dispatching " +
+	            		"link update notifications",
+	            recommendation=LogMessageDoc.GENERIC_ACTION)
+		@Override
+		public void dispatch() {
+			 if (linkDiscoveryAware != null) {
+	                if (log.isTraceEnabled()) {
+	                    log.trace("Dispatching link discovery update {} {} {} {} {} for {}",
+	                              new Object[]{this.getOperation(),
+	                                           HexString.toHexString(this.getSrc()), this.getSrcPort(),
+	                                           HexString.toHexString(this.getDst()), this.getDstPort(),
+	                                           linkDiscoveryAware});
+	                }
+	                try {
+	                    for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order maintained
+	                        lda.linkDiscoveryUpdate(this);
+	                    }
+	                }
+	                catch (Exception e) {
+	                    log.error("Error in link discovery updates loop", e);
+	                }
+	            }
+			
+		}
+    	
+    }
 
     /**
      * List of ports through which LLDP/BDDPs are not sent.
@@ -322,34 +355,7 @@
         return ILinkDiscovery.LinkType.INVALID_LINK;
     }
 
-    @LogMessageDoc(level="ERROR",
-            message="Error in link discovery updates loop",
-            explanation="An unknown error occured while dispatching " +
-            		"link update notifications",
-            recommendation=LogMessageDoc.GENERIC_ACTION)
-    private void doUpdatesThread() throws InterruptedException {
-        do {
-            LDUpdate update = updates.take();
-
-            if (linkDiscoveryAware != null) {
-                if (log.isTraceEnabled()) {
-                    log.trace("Dispatching link discovery update {} {} {} {} {} for {}",
-                              new Object[]{update.getOperation(),
-                                           HexString.toHexString(update.getSrc()), update.getSrcPort(),
-                                           HexString.toHexString(update.getDst()), update.getDstPort(),
-                                           linkDiscoveryAware});
-                }
-                try {
-                    for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order maintained
-                        lda.linkDiscoveryUpdate(update);
-                    }
-                }
-                catch (Exception e) {
-                    log.error("Error in link discovery updates loop", e);
-                }
-            }
-        } while (updates.peek() != null);
-    }
+    
     private boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
         return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, portNumber));
     }
@@ -490,7 +496,10 @@
         if (portUp) operation = UpdateOperation.PORT_UP;
         else operation = UpdateOperation.PORT_DOWN;
 
-        updates.add(new LDUpdate(sw, port, operation));
+        LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
+        
+        
+        controller.publishUpdate(update);
     }
 
     /** 
@@ -1134,10 +1143,11 @@
 
             if (linkChanged) {
                 // find out if the link was added or removed here.
-                updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
+                LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
                                          lt.getDst(), lt.getDstPort(),
                                          getLinkType(lt, newInfo),
                                          updateOperation));
+                controller.publishUpdate(update);
             }
         } finally {
             lock.writeLock().unlock();
@@ -1184,10 +1194,11 @@
                 }
 
                 LinkInfo info = this.links.remove(lt);
-                updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
+                LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
                                          lt.getDst(), lt.getDstPort(),
                                          getLinkType(lt, info),
                                          UpdateOperation.LINK_REMOVED));
+                controller.publishUpdate(update);
 
                 // Update Event History
                 evHistTopoLink(lt.getSrc(),
@@ -1251,8 +1262,8 @@
                     ((byte)OFPortReason.OFPPR_MODIFY.ordinal() ==
                     ps.getReason() && !portEnabled(ps.getDesc()))) {
                 deleteLinksOnPort(npt, "Port Status Changed");
-                LDUpdate update = new LDUpdate(sw, port, UpdateOperation.PORT_DOWN);
-                updates.add(update);
+                LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, UpdateOperation.PORT_DOWN));
+                controller.publishUpdate(update);
                 linkDeleted = true;
                 } 
             else if (ps.getReason() ==
@@ -1284,14 +1295,15 @@
                                 (updatedDstPortState != null)) {
                             // The link is already known to link discovery
                             // manager and the status has changed, therefore
-                            // send an LDUpdate.
+                            // send an LinkUpdate.
                             UpdateOperation operation =
                                     getUpdateOperation(linkInfo.getSrcPortState(),
                                                        linkInfo.getDstPortState());
-                            updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
+                            LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
                                                      lt.getDst(), lt.getDstPort(),
                                                      getLinkType(lt, linkInfo),
                                                      operation));
+                            controller.publishUpdate(update);
                             writeLinkToStorage(lt, linkInfo);
                             linkInfoChanged = true;
                         }
@@ -1300,7 +1312,8 @@
 
                 UpdateOperation operation =
                         getUpdateOperation(ps.getDesc().getState());
-                updates.add(new LDUpdate(sw, port, operation));
+                LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
+                controller.publishUpdate(update);
             }
 
             if (!linkDeleted && !linkInfoChanged){
@@ -1372,14 +1385,13 @@
         }
         // Update event history
         evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None");
-        LDUpdate update = new LDUpdate(sw.getId(), null,
-                                       UpdateOperation.SWITCH_UPDATED);
-        updates.add(update);
+        LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), null,
+                                       UpdateOperation.SWITCH_UPDATED));
+        controller.publishUpdate(update);
     }
 
     /**
      * When a switch disconnects we remove any links from our map and notify.
-     * @param The id of the switch
      */
     @Override
     public void removedSwitch(IOFSwitch iofSwitch) {
@@ -1399,8 +1411,8 @@
                 deleteLinks(eraseList, "Switch Removed");
 
                 // Send a switch removed update
-                LDUpdate update = new LDUpdate(sw, null, UpdateOperation.SWITCH_REMOVED);
-                updates.add(update);
+                LinkUpdate update = new LinkUpdate(new LDUpdate(sw, null, UpdateOperation.SWITCH_REMOVED));
+                controller.publishUpdate(update);
             }
         } finally {
             lock.writeLock().unlock();
@@ -1486,10 +1498,11 @@
                     UpdateOperation operation;
                     operation = getUpdateOperation(info.getSrcPortState(),
                                                    info.getDstPortState());
-                    updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
+                    LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
                                              lt.getDst(), lt.getDstPort(),
                                              getLinkType(lt, info),
                                              operation));
+                    controller.publishUpdate(update);
                 }
             }
 
@@ -1776,16 +1789,18 @@
                 if (log.isTraceEnabled()) {
                     log.trace("SWITCH_IS_CORE_SWITCH set to False for {}", sw);
                 }
-                updates.add(new LDUpdate(sw.getId(), SwitchType.BASIC_SWITCH,
+                LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), SwitchType.BASIC_SWITCH,
                                          UpdateOperation.SWITCH_UPDATED));
+                controller.publishUpdate(update);
             }
             else {
                 sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH, new Boolean(true));
                 if (log.isTraceEnabled()) {
                     log.trace("SWITCH_IS_CORE_SWITCH set to True for {}", sw);
                 }
-                updates.add(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH,
+                LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH,
                                          UpdateOperation.SWITCH_UPDATED));
+                controller.publishUpdate(update);
             }
         }
     }
@@ -1847,7 +1862,6 @@
         // We create this here because there is no ordering guarantee
         this.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>();
         this.lock = new ReentrantReadWriteLock();
-        this.updates = new LinkedBlockingQueue<LDUpdate>();
         this.links = new HashMap<Link, LinkInfo>();
         this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
         this.suppressLinkDiscovery =
@@ -1910,6 +1924,8 @@
         }
 
         ScheduledExecutorService ses = threadPool.getScheduledExecutor();
+        controller =
+                context.getServiceImpl(IFloodlightProviderService.class);
 
         // To be started by the first switch connection
         discoveryTask = new SingletonTask(ses, new Runnable() {
@@ -1953,20 +1969,6 @@
         bddpTask = new SingletonTask(ses, new QuarantineWorker());
         bddpTask.reschedule(BDDP_TASK_INTERVAL, TimeUnit.MILLISECONDS);
 
-        updatesThread = new Thread(new Runnable () {
-            @Override
-            public void run() {
-                while (true) {
-                    try {
-                        doUpdatesThread();
-                    } catch (InterruptedException e) {
-                        return;
-                    }
-                }
-            }}, "Topology Updates");
-        updatesThread.start();
-
-
 
         // Register for the OpenFlow messages we want to receive
         floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCache.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCache.java
new file mode 100644
index 0000000..83a3b55
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCache.java
@@ -0,0 +1,99 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implements a basic ARP cache which maps IPv4 addresses to MAC addresses.
+ * Mappings time out after a short period of time (currently 1 min). We don't
+ * try and refresh the mapping before the entry times out because as a controller
+ * we don't know if the mapping is still needed.
+ */
+
+/* TODO clean out old ARP entries out of the cache periodically. We currently 
+ * don't do this which means the cache memory size will never decrease. We already
+ * have a periodic thread that can be used to do this in ProxyArpManager.
+ */
+class ArpCache {
+	private final static Logger log = LoggerFactory.getLogger(ArpCache.class);
+	
+	private final static long ARP_ENTRY_TIMEOUT = 60000; //ms (1 min)
+	
+	//Protected by locking on the ArpCache object
+	private final Map<InetAddress, ArpCacheEntry> arpCache;
+	
+	private static class ArpCacheEntry {
+		private final MACAddress macAddress;
+		private long timeLastSeen;	
+
+		public ArpCacheEntry(MACAddress macAddress) {
+			this.macAddress = macAddress;
+			this.timeLastSeen = System.currentTimeMillis();
+		}
+
+		public MACAddress getMacAddress() {
+			return macAddress;
+		}
+		
+		public void setTimeLastSeen(long time){
+			timeLastSeen = time;
+		}
+		
+		public boolean isExpired() {
+			return System.currentTimeMillis() - timeLastSeen > ARP_ENTRY_TIMEOUT;
+		}
+	}
+
+	ArpCache() {
+		arpCache = new HashMap<InetAddress, ArpCacheEntry>();
+	}
+
+	synchronized MACAddress lookup(InetAddress ipAddress){	
+		ArpCacheEntry arpEntry = arpCache.get(ipAddress);
+		
+		if (arpEntry == null){
+			return null;
+		}
+		
+		if (arpEntry.isExpired()) {
+			//Entry has timed out so we'll remove it and return null
+			log.trace("Removing expired ARP entry for {}", ipAddress.getHostAddress());
+			
+			arpCache.remove(ipAddress);
+			return null;
+		}
+		
+		return arpEntry.getMacAddress();
+	}
+
+	synchronized void update(InetAddress ipAddress, MACAddress macAddress){
+		ArpCacheEntry arpEntry = arpCache.get(ipAddress);
+		
+		if (arpEntry != null && arpEntry.getMacAddress().equals(macAddress)){
+			arpEntry.setTimeLastSeen(System.currentTimeMillis());
+		}
+		else {
+			arpCache.put(ipAddress, new ArpCacheEntry(macAddress));
+		}
+	}
+	
+	synchronized List<String> getMappings() {
+		List<String> result = new ArrayList<String>(arpCache.size());
+		
+		for (Map.Entry<InetAddress, ArpCacheEntry> entry : arpCache.entrySet()) {
+			result.add(entry.getKey().getHostAddress() + " => " + 
+					entry.getValue().getMacAddress().toString() + 
+					(entry.getValue().isExpired()?" : EXPIRED":" : VALID"));
+		}
+		
+		return result;
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCacheResource.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCacheResource.java
new file mode 100644
index 0000000..252e66e
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCacheResource.java
@@ -0,0 +1,18 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import java.util.List;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+
+public class ArpCacheResource extends ServerResource {
+
+	@Get("json")
+	public List<String> getArpCache() {
+		IProxyArpService arp = (IProxyArpService) getContext().getAttributes().
+				get(IProxyArpService.class.getCanonicalName());
+		
+		return arp.getMappings();
+	}
+
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpTableEntry.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpTableEntry.java
deleted file mode 100644
index 5830cfd..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpTableEntry.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package net.onrc.onos.ofcontroller.proxyarp;
-
-
-public class ArpTableEntry {
-	
-	private byte[] macAddress;
-	private long timeLastSeen;	
-
-	public ArpTableEntry(byte[] macAddress, long timeLastSeen) {
-		this.macAddress = macAddress;
-		this.timeLastSeen = timeLastSeen;
-	}
-
-	public byte[] getMacAddress() {
-		return macAddress;
-	}
-
-	public long getTimeLastSeen() {
-		return timeLastSeen;
-	}
-	
-	public void setTimeLastSeen(long time){
-		//TODO thread safety issues?
-		timeLastSeen = time;
-	}
-
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpWebRoutable.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpWebRoutable.java
new file mode 100644
index 0000000..eefa2db
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpWebRoutable.java
@@ -0,0 +1,22 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+public class ArpWebRoutable implements RestletRoutable {
+
+	@Override
+	public Restlet getRestlet(Context context) {
+		Router router = new Router(context);
+		router.attach("/cache/json", ArpCacheResource.class);
+		return router;
+	}
+
+	@Override
+	public String basePath() {
+		return "/wm/arp";
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java
new file mode 100644
index 0000000..66a17a2
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java
@@ -0,0 +1,21 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import java.net.InetAddress;
+
+import net.floodlightcontroller.util.MACAddress;
+
+/**
+ * Callback interface for modules using the {@link IProxyArpService} to
+ * send ARP requests.
+ *
+ */
+public interface IArpRequester {
+	/**
+	 * Callback method that will be called by the {@link IProxyArpService} 
+	 * when it receives a reply for a request previously submitted by this
+	 * {@code IArpRequester}.
+	 * @param ipAddress The IP address than an ARP request was sent for
+	 * @param macAddress The MAC address mapped to the requested IP address
+	 */
+	public void arpResponse(InetAddress ipAddress, MACAddress macAddress);
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
new file mode 100644
index 0000000..97844d3
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
@@ -0,0 +1,34 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import java.net.InetAddress;
+import java.util.List;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.util.MACAddress;
+
+//Extends IFloodlightService so we can access it from REST API resources
+public interface IProxyArpService extends IFloodlightService{
+	/**
+	 * Returns the MAC address if there is a valid entry in the cache.
+	 * Otherwise returns null.
+	 * @param ipAddress
+	 * @return
+	 */
+	public MACAddress getMacAddress(InetAddress ipAddress);
+	
+	/**
+	 * Tell the IProxyArpService to send an ARP request for the IP address.
+	 * The request will be broadcast out all edge ports in the network.
+	 * @param ipAddress
+	 * @param requester
+	 * @param retry Whether to keep sending requests until the MAC is learnt
+	 */
+	public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
+			boolean retry);
+	
+	/**
+	 * Returns a snapshot of the entire ARP cache.
+	 * @return
+	 */
+	public List<String> getMappings();
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
index a5246c0..b6a9591 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -5,10 +5,12 @@
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
 
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.IFloodlightProviderService;
@@ -16,9 +18,12 @@
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.packet.ARP;
 import net.floodlightcontroller.packet.Ethernet;
+import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.topology.ITopologyService;
-import net.floodlightcontroller.topology.NodePortTuple;
 import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.bgproute.ILayer3InfoService;
+import net.onrc.onos.ofcontroller.bgproute.Interface;
 
 import org.openflow.protocol.OFMessage;
 import org.openflow.protocol.OFPacketIn;
@@ -31,27 +36,153 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ProxyArpManager implements IOFMessageListener {
-	private static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+
+public class ProxyArpManager implements IProxyArpService, IOFMessageListener {
+	private final static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
 	
-	private final long ARP_ENTRY_TIMEOUT = 600000; //ms (== 5 mins)
+	private final long ARP_TIMER_PERIOD = 60000; //ms (== 1 min) 
 	
-	protected IFloodlightProviderService floodlightProvider;
-	protected ITopologyService topology;
+	private static final int ARP_REQUEST_TIMEOUT = 2000; //ms
+			
+	private final IFloodlightProviderService floodlightProvider;
+	private final ITopologyService topology;
+	private final ILayer3InfoService layer3;
+	private final IRestApiService restApi;
 	
-	protected Map<InetAddress, ArpTableEntry> arpTable;
+	private final ArpCache arpCache;
+
+	private final SetMultimap<InetAddress, ArpRequest> arpRequests;
+	
+	private static class ArpRequest {
+		private final IArpRequester requester;
+		private final boolean retry;
+		private long requestTime;
+		
+		public ArpRequest(IArpRequester requester, boolean retry){
+			this.requester = requester;
+			this.retry = retry;
+			this.requestTime = System.currentTimeMillis();
+		}
+		
+		public ArpRequest(ArpRequest old) {
+			this.requester = old.requester;
+			this.retry = old.retry;
+			this.requestTime = System.currentTimeMillis();
+		}
+		
+		public boolean isExpired() {
+			return (System.currentTimeMillis() - requestTime) > ARP_REQUEST_TIMEOUT;
+		}
+		
+		public boolean shouldRetry() {
+			return retry;
+		}
+		
+		public void dispatchReply(InetAddress ipAddress, MACAddress replyMacAddress) {
+			requester.arpResponse(ipAddress, replyMacAddress);
+		}
+	}
+	
+	private class HostArpRequester implements IArpRequester {
+		private final ARP arpRequest;
+		private final long dpid;
+		private final short port;
+		
+		public HostArpRequester(ARP arpRequest, long dpid, short port) {
+			this.arpRequest = arpRequest;
+			this.dpid = dpid;
+			this.port = port;
+		}
+
+		@Override
+		public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
+			ProxyArpManager.this.sendArpReply(arpRequest, dpid, port, macAddress);
+		}
+	}
 	
 	public ProxyArpManager(IFloodlightProviderService floodlightProvider,
-				ITopologyService topology){
+				ITopologyService topology, ILayer3InfoService layer3,
+				IRestApiService restApi){
 		this.floodlightProvider = floodlightProvider;
 		this.topology = topology;
+		this.layer3 = layer3;
+		this.restApi = restApi;
 		
-		arpTable = new HashMap<InetAddress, ArpTableEntry>();
+		arpCache = new ArpCache();
+
+		arpRequests = Multimaps.synchronizedSetMultimap(
+				HashMultimap.<InetAddress, ArpRequest>create());
+	}
+	
+	public void startUp() {
+		restApi.addRestletRoutable(new ArpWebRoutable());
+		
+		Timer arpTimer = new Timer("arp-processing");
+		arpTimer.scheduleAtFixedRate(new TimerTask() {
+			@Override
+			public void run() {
+				doPeriodicArpProcessing();
+			}
+		}, 0, ARP_TIMER_PERIOD);
+	}
+	
+	/*
+	 * Function that runs periodically to manage the asynchronous request mechanism.
+	 * It basically cleans up old ARP requests if we don't get a response for them.
+	 * The caller can designate that a request should be retried indefinitely, and
+	 * this task will handle that as well.
+	 */
+	private void doPeriodicArpProcessing() {
+		SetMultimap<InetAddress, ArpRequest> retryList 
+				= HashMultimap.<InetAddress, ArpRequest>create();
+
+		//Have to synchronize externally on the Multimap while using an iterator,
+		//even though it's a synchronizedMultimap
+		synchronized (arpRequests) {
+			log.debug("Current have {} outstanding requests", 
+					arpRequests.size());
+			
+			Iterator<Map.Entry<InetAddress, ArpRequest>> it 
+				= arpRequests.entries().iterator();
+			
+			while (it.hasNext()) {
+				Map.Entry<InetAddress, ArpRequest> entry
+						= it.next();
+				ArpRequest request = entry.getValue();
+				if (request.isExpired()) {
+					log.debug("Cleaning expired ARP request for {}", 
+							entry.getKey().getHostAddress());
+		
+					it.remove();
+					
+					if (request.shouldRetry()) {
+						retryList.put(entry.getKey(), request);
+					}
+				}
+			}
+		}
+		
+		for (Map.Entry<InetAddress, Collection<ArpRequest>> entry 
+				: retryList.asMap().entrySet()) {
+			
+			InetAddress address = entry.getKey();
+			
+			log.debug("Resending ARP request for {}", address.getHostAddress());
+			
+			sendArpRequestForAddress(address);
+			
+			for (ArpRequest request : entry.getValue()) {
+				arpRequests.put(address, new ArpRequest(request));
+			}
+		}
 	}
 	
 	@Override
 	public String getName() {
-		return  "ProxyArpManager";
+		return "ProxyArpManager";
 	}
 
 	@Override
@@ -78,91 +209,190 @@
                 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
 		
 		if (eth.getEtherType() == Ethernet.TYPE_ARP){
-			
-			
 			ARP arp = (ARP) eth.getPayload();
 			
 			if (arp.getOpCode() == ARP.OP_REQUEST) {
-				log.debug("ARP request received for {}", bytesToStringAddr(arp.getTargetProtocolAddress()));
-				
-				byte[] mac = lookupArpTable(arp.getTargetProtocolAddress());
-				
-				if (mac == null){
-					//Mac address is not in our arp table. 
-					//We need to flood the request out edge ports
-					Set<NodePortTuple> broadcastPorts = topology.getBroadcastDomainPorts();
-					log.debug("size {}", broadcastPorts.size());
-					for (NodePortTuple nodePort : broadcastPorts){
-						log.debug("Port {}", nodePort);
-					}
-					broadcastArpRequestOutEdge(pi, sw.getId(), pi.getInPort());
-				}
-				else {
-					//We know the address, so send a reply
-					log.debug("Sending reply of {}", MACAddress.valueOf(mac).toString());
-					sendArpReply(arp, pi, mac, sw);
-				}
+				handleArpRequest(sw, pi, arp);
 			}
 			else if (arp.getOpCode() == ARP.OP_REPLY) {
-				log.debug("ARP reply recieved for {}", bytesToStringAddr(arp.getSenderProtocolAddress()));
-				
-				log.debug("arp table {}", arpTable.keySet());
-				
-				updateArpTable(arp);
+				handleArpReply(sw, pi, arp);
 			}
 		}
 		
+		//TODO should we propagate ARP or swallow it?
+		//Always propagate for now so DeviceManager can learn the host location
 		return Command.CONTINUE;
 	}
-
-	private synchronized byte[] lookupArpTable(byte[] ipAddress){
-		InetAddress addr;
-		try {
-			addr = InetAddress.getByAddress(ipAddress);
-		} catch (UnknownHostException e) {
-			log.warn("Unable to create InetAddress", e);
-			return null;
-		}
-		
-		ArpTableEntry arpEntry = arpTable.get(addr);
-		
-		if (arpEntry == null){
-			return null;
-		}
-		
-		if (System.currentTimeMillis() - arpEntry.getTimeLastSeen() 
-				> ARP_ENTRY_TIMEOUT){
-			//Entry has timed out so we'll remove it and return null
-			arpTable.remove(addr);
-			return null;
-		}
-		
-		return arpEntry.getMacAddress();
-	}
 	
-	private synchronized void updateArpTable(ARP arp){
-		InetAddress addr;
+	private void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp) {
+		if (log.isTraceEnabled()) {
+			log.trace("ARP request received for {}", 
+					inetAddressToString(arp.getTargetProtocolAddress()));
+		}
+
+		InetAddress target;
 		try {
-			addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
+			 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
 		} catch (UnknownHostException e) {
-			log.warn("Unable to create InetAddress", e);
+			log.debug("Invalid address in ARP request", e);
+			return;
+		}
+
+		if (layer3.fromExternalNetwork(sw.getId(), pi.getInPort())) {
+			//If the request came from outside our network, we only care if
+			//it was a request for one of our interfaces.
+			if (layer3.isInterfaceAddress(target)) {
+				log.trace("ARP request for our interface. Sending reply {} => {}",
+						target.getHostAddress(), layer3.getRouterMacAddress());
+				
+				sendArpReply(arp, sw.getId(), pi.getInPort(), 
+						layer3.getRouterMacAddress());
+			}
+			
 			return;
 		}
 		
-		ArpTableEntry arpEntry = arpTable.get(addr);
+		MACAddress macAddress = arpCache.lookup(target);
 		
-		if (arpEntry != null 
-				&& arpEntry.getMacAddress() == arp.getSenderHardwareAddress()){
-			arpEntry.setTimeLastSeen(System.currentTimeMillis());
+		if (macAddress == null){
+			//MAC address is not in our ARP cache.
+			
+			//Record where the request came from so we know where to send the reply
+			arpRequests.put(target, new ArpRequest(
+					new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
+						
+			//Flood the request out edge ports
+			sendArpRequestToSwitches(target, pi.getPacketData(), sw.getId(), pi.getInPort());
 		}
 		else {
-			arpTable.put(addr, 
-					new ArpTableEntry(arp.getSenderHardwareAddress(), 
-										System.currentTimeMillis()));
+			//We know the address, so send a reply
+			if (log.isTraceEnabled()) {
+				log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
+						inetAddressToString(arp.getTargetProtocolAddress()),
+						macAddress.toString(),
+						HexString.toHexString(sw.getId()), pi.getInPort()});
+			}
+			
+			sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
 		}
 	}
 	
-	private void broadcastArpRequestOutEdge(OFPacketIn pi, long inSwitch, short inPort){
+	private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
+		if (log.isTraceEnabled()) {
+			log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] { 
+					inetAddressToString(arp.getSenderProtocolAddress()),
+					HexString.toHexString(arp.getSenderHardwareAddress()),
+					HexString.toHexString(sw.getId()), pi.getInPort()});
+		}
+		
+		InetAddress senderIpAddress;
+		try {
+			senderIpAddress = InetAddress.getByAddress(arp.getSenderProtocolAddress());
+		} catch (UnknownHostException e) {
+			log.debug("Invalid address in ARP reply", e);
+			return;
+		}
+		
+		MACAddress senderMacAddress = MACAddress.valueOf(arp.getSenderHardwareAddress());
+		
+		arpCache.update(senderIpAddress, senderMacAddress);
+		
+		//See if anyone's waiting for this ARP reply
+		Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
+		
+		//Synchronize on the Multimap while using an iterator for one of the sets
+		List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(requests.size());
+		synchronized (arpRequests) {
+			Iterator<ArpRequest> it = requests.iterator();
+			while (it.hasNext()) {
+				ArpRequest request = it.next();
+				it.remove();
+				requestsToSend.add(request);
+			}
+		}
+		
+		//Don't hold an ARP lock while dispatching requests
+		for (ArpRequest request : requestsToSend) {
+			request.dispatchReply(senderIpAddress, senderMacAddress);
+		}
+	}
+	
+	private void sendArpRequestForAddress(InetAddress ipAddress) {
+		//TODO what should the sender IP address and MAC address be if no
+		//IP addresses are configured? Will there ever be a need to send
+		//ARP requests from the controller in that case?
+		//All-zero MAC address doesn't seem to work - hosts don't respond to it
+		
+		byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
+		byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+		byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
+		byte[] broadcastMac = {(byte)0xff, (byte)0xff, (byte)0xff, 
+				(byte)0xff, (byte)0xff, (byte)0xff};
+		
+		ARP arpRequest = new ARP();
+		
+		arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
+			.setProtocolType(ARP.PROTO_TYPE_IP)
+			.setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
+			.setProtocolAddressLength((byte)IPv4.ADDRESS_LENGTH)
+			.setOpCode(ARP.OP_REQUEST)
+			.setTargetHardwareAddress(zeroMac)
+			.setTargetProtocolAddress(ipAddress.getAddress());
+
+		MACAddress routerMacAddress = layer3.getRouterMacAddress();
+		//TODO hack for now as it's unclear what the MAC address should be
+		byte[] senderMacAddress = genericNonZeroMac;
+		if (routerMacAddress != null) {
+			senderMacAddress = routerMacAddress.toBytes();
+		}
+		arpRequest.setSenderHardwareAddress(senderMacAddress);
+		
+		byte[] senderIPAddress = zeroIpv4;
+		Interface intf = layer3.getOutgoingInterface(ipAddress);
+		if (intf != null) {
+			senderIPAddress = intf.getIpAddress().getAddress();
+		}
+		
+		arpRequest.setSenderProtocolAddress(senderIPAddress);
+		
+		Ethernet eth = new Ethernet();
+		eth.setSourceMACAddress(senderMacAddress)
+			.setDestinationMACAddress(broadcastMac)
+			.setEtherType(Ethernet.TYPE_ARP)
+			.setPayload(arpRequest);
+		
+		sendArpRequestToSwitches(ipAddress, eth.serialize());
+	}
+	
+	private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
+		sendArpRequestToSwitches(dstAddress, arpRequest, 
+				0, OFPort.OFPP_NONE.getValue());
+	}
+	
+	private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
+			long inSwitch, short inPort) {
+
+		if (layer3.hasLayer3Configuration()) {
+			Interface intf = layer3.getOutgoingInterface(dstAddress);
+			if (intf != null) {
+				sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
+			}
+			else {
+				//TODO here it should be broadcast out all non-interface edge ports.
+				//I think we can assume that if it's not a request for an external 
+				//network, it's an ARP for a host in our own network. So we want to 
+				//send it out all edge ports that don't have an interface configured
+				//to ensure it reaches all hosts in our network.
+				log.debug("No interface found to send ARP request for {}",
+						dstAddress.getHostAddress());
+			}
+		}
+		else {
+			broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
+		}
+	}
+	
+	private void broadcastArpRequestOutEdge(byte[] arpRequest, long inSwitch, short inPort) {
 		for (IOFSwitch sw : floodlightProvider.getSwitches().values()){
 			Collection<Short> enabledPorts = sw.getEnabledPortNumbers();
 			Set<Short> linkPorts = topology.getPortsWithLinks(sw.getId());
@@ -176,7 +406,7 @@
 			OFPacketOut po = new OFPacketOut();
 			po.setInPort(OFPort.OFPP_NONE)
 				.setBufferId(-1)
-				.setPacketData(pi.getPacketData());
+				.setPacketData(arpRequest);
 				
 			List<OFAction> actions = new ArrayList<OFAction>();
 			
@@ -189,14 +419,13 @@
 				}
 				
 				actions.add(new OFActionOutput(portNum));
-				log.debug("Broadcasting out {}/{}", HexString.toHexString(sw.getId()), portNum);
 			}
 			
 			po.setActions(actions);
 			short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
 			po.setActionsLength(actionsLength);
 			po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength 
-					+ pi.getPacketData().length);
+					+ arpRequest.length);
 			
 			List<OFMessage> msgList = new ArrayList<OFMessage>();
 			msgList.add(po);
@@ -210,26 +439,67 @@
 		}
 	}
 	
-	private void sendArpReply(ARP arpRequest, OFPacketIn pi, byte[] macRequested, IOFSwitch sw){
+	private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
+		if (log.isTraceEnabled()) {
+			log.trace("Sending ARP request out {}/{}", 
+					HexString.toHexString(dpid), port);
+		}
+		
+		OFPacketOut po = new OFPacketOut();
+		po.setInPort(OFPort.OFPP_NONE)
+			.setBufferId(-1)
+			.setPacketData(arpRequest);
+			
+		List<OFAction> actions = new ArrayList<OFAction>();
+		actions.add(new OFActionOutput(port));
+		po.setActions(actions);
+		short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
+		po.setActionsLength(actionsLength);
+		po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength 
+				+ arpRequest.length);
+		
+		IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+		
+		if (sw == null) {
+			log.warn("Switch not found when sending ARP request");
+			return;
+		}
+		
+		try {
+			sw.write(po, null);
+			sw.flush();
+		} catch (IOException e) {
+			log.error("Failure writing packet out to switch", e);
+		}
+	}
+	
+	private void sendArpReply(ARP arpRequest, long dpid, short port, MACAddress targetMac) {
+		if (log.isTraceEnabled()) {
+			log.trace("Sending reply {} => {} to {}", new Object[] {
+					inetAddressToString(arpRequest.getTargetProtocolAddress()),
+					targetMac,
+					inetAddressToString(arpRequest.getSenderProtocolAddress())});
+		}
+		
 		ARP arpReply = new ARP();
 		arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
 			.setProtocolType(ARP.PROTO_TYPE_IP)
 			.setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
-			.setProtocolAddressLength((byte)4) //can't find the constant anywhere
+			.setProtocolAddressLength((byte)IPv4.ADDRESS_LENGTH)
 			.setOpCode(ARP.OP_REPLY)
-			.setSenderHardwareAddress(macRequested)
+			.setSenderHardwareAddress(targetMac.toBytes())
 			.setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
 			.setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
 			.setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
 		
 		Ethernet eth = new Ethernet();
 		eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
-			.setSourceMACAddress(macRequested)
+			.setSourceMACAddress(targetMac.toBytes())
 			.setEtherType(Ethernet.TYPE_ARP)
 			.setPayload(arpReply);
 		
 		List<OFAction> actions = new ArrayList<OFAction>();
-		actions.add(new OFActionOutput(pi.getInPort()));
+		actions.add(new OFActionOutput(port));
 		
 		OFPacketOut po = new OFPacketOut();
 		po.setInPort(OFPort.OFPP_NONE)
@@ -242,27 +512,54 @@
 		
 		List<OFMessage> msgList = new ArrayList<OFMessage>();
 		msgList.add(po);
+
+		IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+		
+		if (sw == null) {
+			log.warn("Switch {} not found when sending ARP reply", 
+					HexString.toHexString(dpid));
+			return;
+		}
 		
 		try {
-			log.debug("Sending ARP reply to {}/{}", HexString.toHexString(sw.getId()), pi.getInPort());
 			sw.write(msgList, null);
 			sw.flush();
 		} catch (IOException e) {
-			log.warn("Failure writing packet out to switch", e);
+			log.error("Failure writing packet out to switch", e);
 		}
 	}
-
-	//TODO this should be put somewhere more central. I use it in BgpRoute as well.
-	//We need a HexString.toHexString() equivalent.
-	private String bytesToStringAddr(byte[] bytes){
-		InetAddress addr;
+	
+	private String inetAddressToString(byte[] bytes) {
 		try {
-			addr = InetAddress.getByAddress(bytes);
+			return InetAddress.getByAddress(bytes).getHostAddress();
 		} catch (UnknownHostException e) {
-			log.warn(" ", e);
+			log.debug("Invalid IP address", e);
 			return "";
 		}
-		if (addr == null) return "";
-		else return addr.getHostAddress();
+	}
+	
+	/*
+	 * IProxyArpService methods
+	 */
+
+	@Override
+	public MACAddress getMacAddress(InetAddress ipAddress) {
+		return arpCache.lookup(ipAddress);
+	}
+
+	@Override
+	public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
+			boolean retry) {
+		arpRequests.put(ipAddress, new ArpRequest(requester, retry));
+		
+		//Sanity check to make sure we don't send a request for our own address
+		if (!layer3.isInterfaceAddress(ipAddress)) {
+			sendArpRequestForAddress(ipAddress);
+		}
+	}
+	
+	@Override
+	public List<String> getMappings() {
+		return arpCache.getMappings();
 	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/routing/TopoRouteService.java b/src/main/java/net/onrc/onos/ofcontroller/routing/TopoRouteService.java
index 59e76ca..21381d9 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/routing/TopoRouteService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/routing/TopoRouteService.java
@@ -1,7 +1,5 @@
 package net.onrc.onos.ofcontroller.routing;
 
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -27,8 +25,6 @@
 
 import com.tinkerpop.blueprints.Direction;
 import com.tinkerpop.blueprints.Vertex;
-import com.tinkerpop.pipes.PipeFunction;
-import com.tinkerpop.pipes.branch.LoopPipe.LoopBundle;
 
 
 /**
@@ -202,6 +198,10 @@
 	    // The local Port info
 	    //
 	    for (Vertex myPortVertex : nodeVertex.getVertices(Direction.OUT, "on")) {
+		// Ignore inactive ports
+		if (! myPortVertex.getProperty("state").toString().equals("ACTIVE"))
+		    continue;
+
 		short myPort = 0;
 		Object obj = myPortVertex.getProperty("number");
 		if (obj instanceof Short) {
@@ -215,6 +215,10 @@
 		// The neighbor Port info
 		//
 		for (Vertex neighborPortVertex : myPortVertex.getVertices(Direction.OUT, "link")) {
+		    // Ignore inactive ports
+		    if (! neighborPortVertex.getProperty("state").toString().equals("ACTIVE"))
+			continue;
+
 		    short neighborPort = 0;
 		    obj = neighborPortVertex.getProperty("number");
 		    if (obj instanceof Short) {
@@ -448,7 +452,15 @@
 		break;
 	    }
 	    for (Vertex parentPort : nextVertex.getVertices(Direction.OUT, "on")) {
+		// Ignore inactive ports
+		if (! parentPort.getProperty("state").toString().equals("ACTIVE"))
+			continue;
+
 		for (Vertex childPort : parentPort.getVertices(Direction.OUT, "link")) {
+		    // Ignore inactive ports
+		    if (! childPort.getProperty("state").toString().equals("ACTIVE"))
+			continue;
+
 		    for (Vertex child : childPort.getVertices(Direction.IN, "on")) {
 			// Ignore inactive switches
 			String state = child.getProperty("state").toString();
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java b/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java
index 5f96414..dec70e3 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java
@@ -5,7 +5,7 @@
 import org.codehaus.jackson.annotate.JsonProperty;
 
 /**
- * The class representing the Data Path.
+ * The data forwarding path state from a source to a destination.
  */
 public class DataPath {
     private SwitchPort srcPort;		// The source port
@@ -76,6 +76,31 @@
     }
 
     /**
+     * Apply Flow Path Flags to the pre-computed Data Path.
+     *
+     * @param flowPathFlags the Flow Path Flags to apply.
+     */
+    public void applyFlowPathFlags(FlowPathFlags flowPathFlags) {
+	if (flowPathFlags == null)
+	    return;		// Nothing to do
+
+	// Discard the first Flow Entry
+	if (flowPathFlags.isDiscardFirstHopEntry()) {
+	    if (flowEntries.size() > 0)
+		flowEntries.remove(0);
+	}
+
+	// Keep only the first Flow Entry
+	if (flowPathFlags.isKeepOnlyFirstHopEntry()) {
+	    if (flowEntries.size() > 1) {
+		FlowEntry flowEntry = flowEntries.get(0);
+		flowEntries.clear();
+		flowEntries.add(flowEntry);
+	    }
+	}
+    }
+
+    /**
      * Get a string with the summary of the shortest-path data path
      * computation.
      *
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
index 7dd0699..762d272 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
@@ -1,6 +1,5 @@
 package net.onrc.onos.ofcontroller.util;
 
-import java.util.ArrayList;
 
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonProperty;
@@ -12,10 +11,10 @@
  * support multiple in-ports and multiple out-ports.
  */
 public class FlowEntry {
-    private FlowId flowId;			// FlowID of flowEntry
+    private FlowId flowId;			// FlowID of the Flow Entry
     private FlowEntryId flowEntryId;		// The Flow Entry ID
     private FlowEntryMatch flowEntryMatch;	// The Flow Entry Match
-    private ArrayList<FlowEntryAction> flowEntryActions; // The Flow Entry Actions
+    private FlowEntryActions flowEntryActions;	// The Flow Entry Actions
     private Dpid dpid;				// The Switch DPID
     private Port inPort;		// The Switch incoming port. Used only
 					// when the entry is used to return
@@ -53,64 +52,64 @@
 	flowEntryMatch.enableDstTcpUdpPort((short)80);
 
 	FlowEntryAction action = null;
-	ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
+	FlowEntryActions actions = new FlowEntryActions();
 
 	action = new FlowEntryAction();
 	action.setActionOutput(new Port((short)12));
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionOutputToController((short)13);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetVlanId((short)14);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetVlanPriority((byte)15);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionStripVlan(true);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetEthernetSrcAddr(mac);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetEthernetDstAddr(mac);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetIPv4SrcAddr(ipv4);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetIPv4DstAddr(ipv4);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetIpToS((byte)16);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetTcpUdpSrcPort((short)17);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetTcpUdpDstPort((short)18);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionEnqueue(new Port((short)19), 20);
-	actions.add(action);
+	actions.addAction(action);
 
 	setFlowEntryActions(actions);
 	*/
 
-
+	flowEntryActions = new FlowEntryActions();
 	flowEntryUserState = FlowEntryUserState.FE_USER_UNKNOWN;
 	flowEntrySwitchState = FlowEntrySwitchState.FE_SWITCH_UNKNOWN;
     }
@@ -173,7 +172,7 @@
      * @return the Flow Entry Actions.
      */
     @JsonProperty("flowEntryActions")
-    public ArrayList<FlowEntryAction> flowEntryActions() {
+    public FlowEntryActions flowEntryActions() {
 	return flowEntryActions;
     }
 
@@ -183,7 +182,7 @@
      * @param flowEntryActions the Flow Entry Actions to set.
      */
     @JsonProperty("flowEntryActions")
-    public void setFlowEntryActions(ArrayList<FlowEntryAction> flowEntryActions) {
+    public void setFlowEntryActions(FlowEntryActions flowEntryActions) {
 	this.flowEntryActions = flowEntryActions;
     }
 
@@ -319,27 +318,39 @@
      * Convert the flow entry to a string.
      *
      * The string has the following form:
-     *  [flowEntryId=XXX flowEntryMatch=XXX flowEntryAction=XXX
-     *   flowEntryAction=XXX flowEntryAction=XXX dpid=XXX
+     *  [flowEntryId=XXX flowEntryMatch=XXX flowEntryActions=XXX dpid=XXX
      *   inPort=XXX outPort=XXX flowEntryUserState=XXX flowEntrySwitchState=XXX
      *   flowEntryErrorState=XXX]
      * @return the flow entry as a string.
      */
     @Override
     public String toString() {
-	String ret = "[flowEntryId=" + this.flowEntryId.toString();
-	ret += " flowEntryMatch=" + this.flowEntryMatch.toString();
-	for (FlowEntryAction fa : flowEntryActions) {
-	    ret += " flowEntryAction=" + fa.toString();
+	StringBuilder ret = new StringBuilder();
+	if ( flowEntryId != null ) {
+		ret.append("[flowEntryId=" + this.flowEntryId.toString());
+	} else {
+		ret.append("[");
 	}
-	ret += " dpid=" + this.dpid.toString();
-	ret += " inPort=" + this.inPort.toString();
-	ret += " outPort=" + this.outPort.toString();
-	ret += " flowEntryUserState=" + this.flowEntryUserState;
-	ret += " flowEntrySwitchState=" + this.flowEntrySwitchState;
-	ret += " flowEntryErrorState=" + this.flowEntryErrorState.toString();
-	ret += "]";
+	if ( flowEntryMatch != null ) {
+		ret.append(" flowEntryMatch=" + this.flowEntryMatch.toString());
+	}
+	ret.append( " flowEntryActions=" + this.flowEntryActions.toString() );
+	if ( dpid != null ) {
+		ret.append(" dpid=" + this.dpid.toString());
+	}
+	if ( inPort != null ) {
+		ret.append(" inPort=" + this.inPort.toString());
+	}
+	if ( outPort != null ) {
+		ret.append(" outPort=" + this.outPort.toString());
+	}
+	ret.append(" flowEntryUserState=" + this.flowEntryUserState);
+	ret.append(" flowEntrySwitchState=" + this.flowEntrySwitchState);
+	if ( flowEntryErrorState != null ) {
+		ret.append(" flowEntryErrorState=" + this.flowEntryErrorState.toString());
+	}
+	ret.append("]");
 
-	return ret;
+	return ret.toString();
     }
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryAction.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryAction.java
index 22aef98..1f8849a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryAction.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryAction.java
@@ -7,7 +7,9 @@
 /**
  * The class representing a single Flow Entry action.
  *
- * A set of Flow Entry actions need to be applied to each packet.
+ * A Flow Entry action that needs to be applied to each packet.
+ * Note that it contains only a single action. Multiple actions are
+ * listed in a list inside @ref FlowEntryActions.
  */
 public class FlowEntryAction {
     /**
@@ -59,6 +61,28 @@
 	    this.maxLen = 0;
 	}
 
+	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionOutput(ActionOutput other) {
+	    if (other.port != null)
+		this.port = new Port(other.port);
+	    this.maxLen = other.maxLen;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX maxLen=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionOutput(String actionStr) {
+	    this.fromString(actionStr);
+	}
 
 	/**
 	 * Constructor for a given output port and maximum length.
@@ -121,6 +145,54 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX maxLen=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split(" ");
+	    String decode = null;
+
+	    // Decode the "port=XXX" part
+	    if (parts.length > 0)
+		decode = parts[0];
+	    if (decode != null) {
+		String[] tokens = decode.split("port=");
+		if (tokens.length > 1 && tokens[1] != null) {
+		    try {
+			Short valueShort = Short.valueOf(tokens[1]);
+			port = new Port(valueShort);
+		    } catch (NumberFormatException e) {
+			throw new IllegalArgumentException("Invalid action string");
+		    }
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+
+	    // Decode the "maxLen=XXX" part
+	    decode = null;
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		String[] tokens = decode.split("maxLen=");
+		if (tokens.length > 1 && tokens[1] != null) {
+		    try {
+			maxLen = Short.valueOf(tokens[1]);
+		    } catch (NumberFormatException e) {
+			throw new IllegalArgumentException("Invalid action string");
+		    }
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -137,6 +209,27 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetVlanId(ActionSetVlanId other) {
+	    this.vlanId = other.vlanId;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [vlanId=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetVlanId(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given VLAN ID.
 	 *
 	 * @param vlanId the VLAN ID to set.
@@ -171,6 +264,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [vlanId=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("vlanId=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    vlanId = Short.valueOf(decode);
+		} catch (NumberFormatException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -187,6 +307,27 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetVlanPriority(ActionSetVlanPriority other) {
+	    this.vlanPriority = other.vlanPriority;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [vlanPriority=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetVlanPriority(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given VLAN priority.
 	 *
 	 * @param vlanPriority the VLAN priority to set.
@@ -221,6 +362,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [vlanPriority=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("vlanPriority=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    vlanPriority = Byte.valueOf(decode);
+		} catch (NumberFormatException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -237,6 +405,27 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionStripVlan(ActionStripVlan other) {
+	    this.stripVlan = other.stripVlan;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [stripVlan=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionStripVlan(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given boolean flag.
 	 *
 	 * @param stripVlan if true, strip the VLAN header.
@@ -271,6 +460,29 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [stripVlan=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("stripVlan=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		stripVlan = Boolean.valueOf(decode);
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -288,6 +500,28 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetEthernetAddr(ActionSetEthernetAddr other) {
+	    if (other.addr != null)
+		this.addr = MACAddress.valueOf(other.addr.toLong());
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [addr=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetEthernetAddr(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given MAC address.
 	 *
 	 * @param addr the MAC address to set.
@@ -322,6 +556,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [addr=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("addr=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    addr = MACAddress.valueOf(decode);
+		} catch (IllegalArgumentException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -339,6 +600,28 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetIPv4Addr(ActionSetIPv4Addr other) {
+	    if (other.addr != null)
+		this.addr = new IPv4(other.addr);
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [addr=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetIPv4Addr(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given IPv4 address.
 	 *
 	 * @param addr the IPv4 address to set.
@@ -373,6 +656,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [addr=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("addr=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    addr = new IPv4(decode);
+		} catch (IllegalArgumentException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -390,6 +700,27 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetIpToS(ActionSetIpToS other) {
+	    this.ipToS = other.ipToS;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [ipToS=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetIpToS(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given IP ToS (DSCP field, 6 bits).
 	 *
 	 * @param ipToS the IP ToS (DSCP field, 6 bits) to set.
@@ -424,6 +755,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [ipToS=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("ipToS=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    ipToS = Byte.valueOf(decode);
+		} catch (NumberFormatException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -441,6 +799,27 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetTcpUdpPort(ActionSetTcpUdpPort other) {
+	    this.port = other.port;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetTcpUdpPort(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given TCP/UDP port.
 	 *
 	 * @param port the TCP/UDP port to set.
@@ -475,6 +854,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("port=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    port = Short.valueOf(decode);
+		} catch (NumberFormatException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -495,6 +901,29 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionEnqueue(ActionEnqueue other) {
+	    if (other.port != null)
+		this.port = new Port(other.port);
+	    this.queueId = other.queueId;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX queueId=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionEnqueue(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given port and queue ID.
 	 *
 	 * @param port the port to set.
@@ -542,6 +971,54 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX queueId=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split(" ");
+	    String decode = null;
+
+	    // Decode the "port=XXX" part
+	    if (parts.length > 0)
+		decode = parts[0];
+	    if (decode != null) {
+		String[] tokens = decode.split("port=");
+		if (tokens.length > 1 && tokens[1] != null) {
+		    try {
+			Short valueShort = Short.valueOf(tokens[1]);
+			port = new Port(valueShort);
+		    } catch (NumberFormatException e) {
+			throw new IllegalArgumentException("Invalid action string");
+		    }
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+
+	    // Decode the "queueId=XXX" part
+	    decode = null;
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		String[] tokens = decode.split("queueId=");
+		if (tokens.length > 1 && tokens[1] != null) {
+		    try {
+			queueId = Short.valueOf(tokens[1]);
+		    } catch (NumberFormatException e) {
+			throw new IllegalArgumentException("Invalid action string");
+		    }
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     private ActionValues actionType;	// The action type
@@ -571,6 +1048,88 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public FlowEntryAction(FlowEntryAction other) {
+	this.actionType = other.actionType;
+
+	//
+	if (other.actionOutput != null)
+	    this.actionOutput = new ActionOutput(other.actionOutput);
+	else
+	    this.actionOutput = null;
+	//
+	if (other.actionSetVlanId != null)
+	    this.actionSetVlanId = new ActionSetVlanId(other.actionSetVlanId);
+	else
+	    this.actionSetVlanId = null;
+	//
+	if (other.actionSetVlanPriority != null)
+	    this.actionSetVlanPriority = new ActionSetVlanPriority(other.actionSetVlanPriority);
+	else
+	    this.actionSetVlanPriority = null;
+	//
+	if (other.actionStripVlan != null)
+	    this.actionStripVlan = new ActionStripVlan(other.actionStripVlan);
+	else
+	    this.actionStripVlan = null;
+	//
+	if (other.actionSetEthernetSrcAddr != null)
+	    this.actionSetEthernetSrcAddr = new ActionSetEthernetAddr(other.actionSetEthernetSrcAddr);
+	else
+	    this.actionSetEthernetSrcAddr = null;
+	//
+	if (other.actionSetEthernetDstAddr != null)
+	    this.actionSetEthernetDstAddr = new ActionSetEthernetAddr(other.actionSetEthernetDstAddr);
+	else
+	    this.actionSetEthernetDstAddr = null;
+	//
+	if (other.actionSetIPv4SrcAddr != null)
+	    this.actionSetIPv4SrcAddr = new ActionSetIPv4Addr(other.actionSetIPv4SrcAddr);
+	else
+	    this.actionSetIPv4SrcAddr = null;
+	//
+	if (other.actionSetIPv4DstAddr != null)
+	    this.actionSetIPv4DstAddr = new ActionSetIPv4Addr(other.actionSetIPv4DstAddr);
+	else
+	    this.actionSetIPv4DstAddr = null;
+	//
+	if (other.actionSetIpToS != null)
+	    this.actionSetIpToS = new ActionSetIpToS(other.actionSetIpToS);
+	else
+	    this.actionSetIpToS = null;
+	//
+	if (other.actionSetTcpUdpSrcPort != null)
+	    this.actionSetTcpUdpSrcPort = new ActionSetTcpUdpPort(other.actionSetTcpUdpSrcPort);
+	else
+	    this.actionSetTcpUdpSrcPort = null;
+	//
+	if (other.actionSetTcpUdpDstPort != null)
+	    this.actionSetTcpUdpDstPort = new ActionSetTcpUdpPort(other.actionSetTcpUdpDstPort);
+	else
+	    this.actionSetTcpUdpDstPort = null;
+	//
+	if (other.actionEnqueue != null)
+	    this.actionEnqueue = new ActionEnqueue(other.actionEnqueue);
+	else
+	    this.actionEnqueue = null;
+    }
+
+    /**
+     * Constructor from a string.
+     *
+     * The string has the following form:
+     *  [type=XXX action=XXX]
+     *
+     * @param actionStr the action as a string.
+     */
+    public FlowEntryAction(String actionStr) {
+	this.fromString(actionStr);
+    }
+
+    /**
      * Get the action type.
      *
      * @return the action type.
@@ -949,7 +1508,7 @@
      * Set the action to output to queue on a port.
      *
      * @param port the port to set.
-     * @param int queueId the queue ID to set.
+     * @param queueId the queue ID to set.
      */
     public void setActionEnqueue(Port port, int queueId) {
 	actionEnqueue = new ActionEnqueue(port, queueId);
@@ -957,12 +1516,12 @@
     }
 
     /**
-     * Convert the set of actions to a string.
+     * Convert the action to a string.
      *
      * The string has the following form:
      *  [type=XXX action=XXX]
      *
-     * @return the set of actions as a string.
+     * @return the action as a string.
      */
     @Override
     public String toString() {
@@ -1010,4 +1569,96 @@
 
 	return ret;
     }
+
+    /**
+     * Convert a string to an action.
+     *
+     * The string has the following form:
+     *  [type=XXX action=XXX]
+     *
+     * @param actionStr the action as a string.
+     */
+    public void fromString(String actionStr) {
+	String[] parts = actionStr.split("type=");
+	String decode = null;
+
+	// Extract the string after the "type="
+	if (parts.length > 1)
+	    decode = parts[1];
+	if (decode == null)
+	    throw new IllegalArgumentException("Invalid action string");
+
+	// Remove the trailing ']'
+	if ((decode.length() > 0) && (decode.charAt(decode.length() - 1) == ']')) {
+	    decode = decode.substring(0, decode.length() - 1);
+	} else {
+	    throw new IllegalArgumentException("Invalid action string");
+	}
+
+	// Extract the type value and the action value
+	parts = decode.split(" action=");
+
+	// Decode the "type=XXX" payload
+	if (parts.length > 0)
+	    decode = parts[0];
+	if (decode != null) {
+	    try {
+		actionType = Enum.valueOf(ActionValues.class, decode);
+	    } catch (IllegalArgumentException e) {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	} else {
+	    throw new IllegalArgumentException("Invalid action string");
+	}
+
+	// Decode the "action=XXX" payload
+	decode = null;
+	if (parts.length > 1)
+	    decode = parts[1];
+	if (decode == null)
+	    throw new IllegalArgumentException("Invalid action string");
+	//
+	try {
+	    switch (actionType) {
+	    case ACTION_OUTPUT:
+		actionOutput = new ActionOutput(decode);
+		break;
+	    case ACTION_SET_VLAN_VID:
+		actionSetVlanId = new ActionSetVlanId(decode);
+		break;
+	    case ACTION_SET_VLAN_PCP:
+		actionSetVlanPriority = new ActionSetVlanPriority(decode);
+		break;
+	    case ACTION_STRIP_VLAN:
+		actionStripVlan = new ActionStripVlan(decode);
+		break;
+	    case ACTION_SET_DL_SRC:
+		actionSetEthernetSrcAddr = new ActionSetEthernetAddr(decode);
+		break;
+	    case ACTION_SET_DL_DST:
+		actionSetEthernetDstAddr = new ActionSetEthernetAddr(decode);
+		break;
+	    case ACTION_SET_NW_SRC:
+		actionSetIPv4SrcAddr = new ActionSetIPv4Addr(decode);
+		break;
+	    case ACTION_SET_NW_DST:
+		actionSetIPv4DstAddr = new ActionSetIPv4Addr(decode);
+		break;
+	    case ACTION_SET_NW_TOS:
+		actionSetIpToS = new ActionSetIpToS(decode);
+		break;
+	    case ACTION_SET_TP_SRC:
+		actionSetTcpUdpSrcPort = new ActionSetTcpUdpPort(decode);
+		break;
+	    case ACTION_SET_TP_DST:
+		actionSetTcpUdpDstPort = new ActionSetTcpUdpPort(decode);
+		break;
+	    case ACTION_ENQUEUE:
+		actionEnqueue = new ActionEnqueue(decode);
+		break;
+	    }
+	} catch (IllegalArgumentException e) {
+	    throw new IllegalArgumentException("Invalid action string");
+	}
+    }
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryActions.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryActions.java
new file mode 100644
index 0000000..53aab66
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryActions.java
@@ -0,0 +1,146 @@
+package net.onrc.onos.ofcontroller.util;
+
+import java.util.ArrayList;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing multiple Flow Entry actions.
+ *
+ * A set of Flow Entry actions need to be applied to each packet.
+ */
+public class FlowEntryActions {
+    private ArrayList<FlowEntryAction> actions;	// The Flow Entry Actions
+
+    /**
+     * Default constructor.
+     */
+    public FlowEntryActions() {
+	actions = new ArrayList<FlowEntryAction>();
+    }
+
+    /**
+     * Constructor from a string.
+     *
+     * The string has the following form:
+     *  [[type=XXX action=XXX];[type=XXX action=XXX];...;]
+     *
+     * @param actionsStr the set of actions as a string.
+     */
+    public FlowEntryActions(String actionsStr) {
+	this.fromString(actionsStr);
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public FlowEntryActions(FlowEntryActions other) {
+	actions = new ArrayList<FlowEntryAction>();
+
+	for (FlowEntryAction action : other.actions) {
+	    FlowEntryAction newAction = new FlowEntryAction(action);
+	    actions.add(newAction);
+	}
+    }
+
+    /**
+     * Get the Flow Entry Actions.
+     *
+     * @return the Flow Entry Actions.
+     */
+    @JsonProperty("actions")
+    public ArrayList<FlowEntryAction> actions() {
+	return actions;
+    }
+
+    /**
+     * Set the Flow Entry Actions.
+     *
+     * @param actions the Flow Entry Actions to set.
+     */
+    @JsonProperty("actions")
+    public void setActions(ArrayList<FlowEntryAction> actions) {
+	this.actions = actions;
+    }
+
+    /**
+     * Add a Flow Entry Action.
+     *
+     * @param flowEntryAction the Flow Entry Action to add.
+     */
+    public void addAction(FlowEntryAction flowEntryAction) {
+	actions.add(flowEntryAction);
+    }
+
+    /**
+     * Test whether the set of actions is empty.
+     *
+     * @return true if the set of actions is empty, otherwise false.
+     */
+    public Boolean isEmpty() {
+	return actions.isEmpty();
+    }
+
+    /**
+     * Convert the set of actions to a string.
+     *
+     * The string has the following form:
+     *  [[type=XXX action=XXX];[type=XXX action=XXX];...;]
+     *
+     * @return the set of actions as a string.
+     */
+    @Override
+    public String toString() {
+	String ret = "[";
+	for (FlowEntryAction action : actions) {
+	    ret += action.toString() + ";";
+	}
+	ret += "]";
+
+	return ret;
+    }
+
+    /**
+     * Convert a string to a set of actions.
+     *
+     * The string has the following form:
+     *  [[type=XXX action=XXX];[type=XXX action=XXX];...;]
+     *
+     * @param actionsStr the set of actions as a string.
+     */
+    public void fromString(String actionsStr) {
+	String decode = actionsStr;
+
+	actions = new ArrayList<FlowEntryAction>();
+
+	if (decode.isEmpty())
+	    return;		// Nothing to do
+
+	// Remove the '[' and ']' in the beginning and the end of the string
+	if ((decode.length() > 1) && (decode.charAt(0) == '[') &&
+	    (decode.charAt(decode.length() - 1) == ']')) {
+	    decode = decode.substring(1, decode.length() - 1);
+	} else {
+	    throw new IllegalArgumentException("Invalid action string");
+	}
+
+	// Split the string, and decode each action
+	String[] parts = decode.split(";");
+	for (int i = 0; i < parts.length; i++) {
+	    decode = parts[i];
+	    if ((decode == null) || decode.isEmpty())
+		continue;
+	    FlowEntryAction flowEntryAction = null;
+	    try {
+		flowEntryAction = new FlowEntryAction(decode);
+	    } catch (IllegalArgumentException e) {
+		// TODO: Ignore invalid actions for now
+		continue;
+	    }
+	    if (flowEntryAction != null)
+		actions.add(flowEntryAction);
+	}
+    }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
index 4a8fc7b..a56dbff 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
@@ -1,10 +1,9 @@
 package net.onrc.onos.ofcontroller.util;
 
-import java.util.ArrayList;
-
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowPath;
+import net.onrc.onos.ofcontroller.util.FlowPathFlags;
 
 import org.codehaus.jackson.annotate.JsonProperty;
 
@@ -14,15 +13,20 @@
 public class FlowPath implements Comparable<FlowPath> {
     private FlowId flowId;		// The Flow ID
     private CallerId installerId;	// The Caller ID of the path installer
+    private FlowPathFlags flowPathFlags; // The Flow Path flags
     private DataPath dataPath;		// The data path
     private FlowEntryMatch flowEntryMatch; // Common Flow Entry Match for all
 					// Flow Entries
+    private FlowEntryActions flowEntryActions; // The Flow Entry Actions for
+					// the first Flow Entry
 
     /**
      * Default constructor.
      */
     public FlowPath() {
+	flowPathFlags = new FlowPathFlags();
 	dataPath = new DataPath();
+	flowEntryActions = new FlowEntryActions();
     }
 
     /**
@@ -32,6 +36,7 @@
     	dataPath = new DataPath();
     	this.setFlowId(new FlowId(flowObj.getFlowId()));
     	this.setInstallerId(new CallerId(flowObj.getInstallerId()));
+	this.setFlowPathFlags(new FlowPathFlags(flowObj.getFlowPathFlags()));
     	this.dataPath().srcPort().setDpid(new Dpid(flowObj.getSrcSwitch()));
     	this.dataPath().srcPort().setPort(new Port(flowObj.getSrcPort()));
     	this.dataPath().dstPort().setDpid(new Dpid(flowObj.getDstSwitch()));
@@ -78,6 +83,19 @@
     	    this.setFlowEntryMatch(match);
 	}
 
+	//
+	// Extract the actions for the first Flow Entry
+	//
+	{
+	    FlowEntryActions actions = new FlowEntryActions();
+
+	    String actionsStr = flowObj.getActions();
+	    if (actions != null)
+		actions = new FlowEntryActions(actionsStr);
+
+	    this.setFlowEntryActions(actions);
+	}
+
     	//
     	// Extract all Flow Entries
     	//
@@ -91,64 +109,77 @@
     	    // Extract the match conditions
     	    //
     	    FlowEntryMatch match = new FlowEntryMatch();
+	    //
     	    Short matchInPort = flowEntryObj.getMatchInPort();
     	    if (matchInPort != null)
     		match.enableInPort(new Port(matchInPort));
+	    //
     	    String matchSrcMac = flowEntryObj.getMatchSrcMac();
     	    if (matchSrcMac != null)
     		match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
+	    //
     	    String matchDstMac = flowEntryObj.getMatchDstMac();
     	    if (matchDstMac != null)
     		match.enableDstMac(MACAddress.valueOf(matchDstMac));
+	    //
     	    Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
     	    if (matchEthernetFrameType != null)
     		match.enableEthernetFrameType(matchEthernetFrameType);
+	    //
     	    Short matchVlanId = flowEntryObj.getMatchVlanId();
     	    if (matchVlanId != null)
     		match.enableVlanId(matchVlanId);
+	    //
     	    Byte matchVlanPriority = flowEntryObj.getMatchVlanPriority();
     	    if (matchVlanPriority != null)
     		match.enableVlanPriority(matchVlanPriority);
+	    //
     	    String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
     	    if (matchSrcIPv4Net != null)
     		match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
+	    //
     	    String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
     	    if (matchDstIPv4Net != null)
     		match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
+	    //
     	    Byte matchIpProto = flowEntryObj.getMatchIpProto();
     	    if (matchIpProto != null)
     		match.enableIpProto(matchIpProto);
+	    //
     	    Byte matchIpToS = flowEntryObj.getMatchIpToS();
     	    if (matchIpToS != null)
     		match.enableIpToS(matchIpToS);
+	    //
     	    Short matchSrcTcpUdpPort = flowEntryObj.getMatchSrcTcpUdpPort();
     	    if (matchSrcTcpUdpPort != null)
     		match.enableSrcTcpUdpPort(matchSrcTcpUdpPort);
+	    //
     	    Short matchDstTcpUdpPort = flowEntryObj.getMatchDstTcpUdpPort();
     	    if (matchDstTcpUdpPort != null)
     		match.enableDstTcpUdpPort(matchDstTcpUdpPort);
+	    //
     	    flowEntry.setFlowEntryMatch(match);
 
-    	    //
-    	    // Extract the actions
-    	    //
-    	    ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
-    	    Short actionOutputPort = flowEntryObj.getActionOutput();
-    	    if (actionOutputPort != null) {
-    		FlowEntryAction action = new FlowEntryAction();
-    		action.setActionOutput(new Port(actionOutputPort));
-    		actions.add(action);
-    	    }
-    	    flowEntry.setFlowEntryActions(actions);
+	    //
+	    // Extract the actions
+	    //
+	    {
+		FlowEntryActions actions = new FlowEntryActions();
+
+		String actionsStr = flowEntryObj.getActions();
+		if (actions != null)
+		    actions = new FlowEntryActions(actionsStr);
+
+		flowEntry.setFlowEntryActions(actions);
+	    }
 
     	    String userState = flowEntryObj.getUserState();
     	    flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
     	    String switchState = flowEntryObj.getSwitchState();
     	    flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
-    	    //
-    	    // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
-    	    // and FlowEntryErrorState.
-    	    //
+	    //
+	    // TODO: Take care of the FlowEntryErrorState.
+	    //
     	    this.dataPath().flowEntries().add(flowEntry);
     	}
     }
@@ -190,6 +221,24 @@
     }
 
     /**
+     * Get the flow path flags.
+     *
+     * @return the flow path flags.
+     */
+    @JsonProperty("flowPathFlags")
+    public FlowPathFlags flowPathFlags() { return flowPathFlags; }
+
+    /**
+     * Set the flow path flags.
+     *
+     * @param flowPathFlags the flow path flags to set.
+     */
+    @JsonProperty("flowPathFlags")
+    public void setFlowPathFlags(FlowPathFlags flowPathFlags) {
+	this.flowPathFlags = flowPathFlags;
+    }
+
+    /**
      * Get the flow path's data path.
      *
      * @return the flow path's data path.
@@ -227,10 +276,32 @@
     }
 
     /**
+     * Get the flow path's flow entry actions for the first Flow Entry.
+     *
+     * @return the flow path's flow entry actions for the first Flow Entry.
+     */
+    @JsonProperty("flowEntryActions")
+    public FlowEntryActions flowEntryActions() {
+	return flowEntryActions;
+    }
+
+    /**
+     * Set the flow path's flow entry actions for the first Flow Entry.
+     *
+     * @param flowEntryActions the flow path's flow entry actions for the first
+     * Flow Entry.
+     */
+    @JsonProperty("flowEntryActions")
+    public void setFlowEntryActions(FlowEntryActions flowEntryActions) {
+	this.flowEntryActions = flowEntryActions;
+    }
+
+    /**
      * Convert the flow path to a string.
      *
      * The string has the following form:
-     *  [flowId=XXX installerId=XXX dataPath=XXX]
+     *  [flowId=XXX installerId=XXX flowPathFlags=XXX dataPath=XXX
+     *   flowEntryMatch=XXX flowEntryActions=XXX]
      *
      * @return the flow path as a string.
      */
@@ -238,10 +309,13 @@
     public String toString() {
 	String ret = "[flowId=" + this.flowId.toString();
 	ret += " installerId=" + this.installerId.toString();
+	ret += " flowPathFlags=" + this.flowPathFlags.toString();
 	if (dataPath != null)
 	    ret += " dataPath=" + this.dataPath.toString();
 	if (flowEntryMatch != null)
 	    ret += " flowEntryMatch=" + this.flowEntryMatch.toString();
+	if (flowEntryActions != null)
+	    ret += " flowEntryActions=" + this.flowEntryActions.toString();
 	ret += "]";
 	return ret;
     }
@@ -253,5 +327,4 @@
     public int compareTo(FlowPath f) {
     	return (int) (this.flowId.value() - f.flowId.value());
     }
-
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathFlags.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathFlags.java
new file mode 100644
index 0000000..63241f0
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPathFlags.java
@@ -0,0 +1,128 @@
+package net.onrc.onos.ofcontroller.util;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing the Flow Path flags.
+ */
+public class FlowPathFlags {
+    private long flags;
+
+    // Discard the first-hop Flow Entry
+    private final long DISCARD_FIRST_HOP_ENTRY		= (1 << 0);
+
+    // Keep only the first-hop Flow Entry
+    private final long KEEP_ONLY_FIRST_HOP_ENTRY	= (1 << 1);
+
+    /**
+     * Default constructor.
+     */
+    public FlowPathFlags() {
+	this.flags = 0;
+    }
+
+    /**
+     * Constructor for given flags.
+     *
+     * @param flags the flags value to set.
+     */
+    public FlowPathFlags(long flags) {
+	this.flags = flags;
+    }
+
+    /**
+     * Constructor for given flags as a string.
+     *
+     * The string value should contain the name of each flags to set. E.g.:
+     *  "DISCARD_FIRST_HOP_ENTRY,KEEP_ONLY_FIRST_HOP_ENTRY"
+     * @param flagsStr the string value of the flags to set.
+     */
+    public FlowPathFlags(String flagsStr) {
+	this.setFlagsStr(flagsStr);
+    }
+
+    /**
+     * Get the flags.
+     *
+     * @return the flags.
+     */
+    @JsonProperty("flags")
+    public long flags() { return flags; }
+
+    /**
+     * Set the flags.
+     *
+     * @param flags the flags value to set.
+     */
+    @JsonProperty("flags")
+    public void setFlags(long flags) {
+	this.flags = flags;
+    }
+
+    /**
+     * Set the flags as a string.
+     *
+     * The string value should contain the name of each flags to set. E.g.:
+     *  "DISCARD_FIRST_HOP_ENTRY,KEEP_ONLY_FIRST_HOP_ENTRY"
+     * @param flagsStr the string value of the flags to set.
+     */
+    @JsonProperty("flagsStr")
+    public void setFlagsStr(String flagsStr) {
+	this.flags = 0L;
+
+	// Test all flags
+	if (flagsStr.contains("DISCARD_FIRST_HOP_ENTRY"))
+	    this.flags |= DISCARD_FIRST_HOP_ENTRY;
+	if (flagsStr.contains("KEEP_ONLY_FIRST_HOP_ENTRY"))
+	    this.flags |= KEEP_ONLY_FIRST_HOP_ENTRY;
+    }
+
+    /**
+     * Test whether the DISCARD_FIRST_HOP_ENTRY flag is set.
+     *
+     * @return true if the DISCARD_FIRST_HOP_ENTRY flag is set,
+     * otherwise false.
+     */
+    public boolean isDiscardFirstHopEntry() {
+	return ((flags & DISCARD_FIRST_HOP_ENTRY) != 0);
+    }
+
+    /**
+     * Test whether the KEEP_ONLY_FIRST_HOP_ENTRY flag is set.
+     *
+     * @return true if the KEEP_ONLY_FIRST_HOP_ENTRY flag is set,
+     * otherwise false.
+     */
+    public boolean isKeepOnlyFirstHopEntry() {
+	return ((flags & KEEP_ONLY_FIRST_HOP_ENTRY) != 0);
+    }
+
+    /**
+     * Convert the Flow Path Flags to a string.
+     *
+     * The string has the following form:
+     *  [flags=DISCARD_FIRST_HOP_ENTRY,KEEP_ONLY_FIRST_HOP_ENTRY]
+     *
+     * @return the Flow Path flags as a string.
+     */
+    @Override
+    public String toString() {
+	String flagsStr = null;
+	String ret = "[flags=";
+
+	// Test all flags
+	if ((this.flags & DISCARD_FIRST_HOP_ENTRY) != 0) {
+	    if (flagsStr != null)
+		flagsStr += ",";
+	    flagsStr += "DISCARD_FIRST_HOP_ENTRY";
+	}
+	if ((this.flags & KEEP_ONLY_FIRST_HOP_ENTRY) != 0) {
+	    if (flagsStr != null)
+		flagsStr += ",";
+	    flagsStr += "KEEP_ONLY_FIRST_HOP_ENTRY";
+	}
+	ret += "]";
+
+	return ret;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/IPv4.java b/src/main/java/net/onrc/onos/ofcontroller/util/IPv4.java
index 2081bbe..119165c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/IPv4.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/IPv4.java
@@ -22,6 +22,15 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public IPv4(IPv4 other) {
+	this.value = other.value;
+    }
+
+    /**
      * Constructor from an integer value.
      *
      * @param value the value to use.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/IPv4Net.java b/src/main/java/net/onrc/onos/ofcontroller/util/IPv4Net.java
index 52e6535..9ce247c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/IPv4Net.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/IPv4Net.java
@@ -23,6 +23,17 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public IPv4Net(IPv4Net other) {
+	if (other.address != null)
+	    this.address = new IPv4(other.address);
+	this.prefixLen = other.prefixLen;
+    }
+
+    /**
      * Constructor for a given address and prefix length.
      *
      * @param address the address to use.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/IPv6.java b/src/main/java/net/onrc/onos/ofcontroller/util/IPv6.java
index 2d28db8..f5dae11 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/IPv6.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/IPv6.java
@@ -25,6 +25,16 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public IPv6(IPv6 other) {
+	this.valueHigh = other.valueHigh;
+	this.valueLow = other.valueLow;
+    }
+
+    /**
      * Constructor from integer values.
      *
      * @param valueHigh the higher (more significant) 64 bits of the address.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/IPv6Net.java b/src/main/java/net/onrc/onos/ofcontroller/util/IPv6Net.java
index bafff24..65ffe54 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/IPv6Net.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/IPv6Net.java
@@ -23,6 +23,17 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public IPv6Net(IPv6Net other) {
+	if (other.address != null)
+	    this.address = new IPv6(other.address);
+	this.prefixLen = other.prefixLen;
+    }
+
+    /**
      * Constructor for a given address and prefix length.
      *
      * @param address the address to use.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/Port.java b/src/main/java/net/onrc/onos/ofcontroller/util/Port.java
index 34c807d..bb4851c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/Port.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/Port.java
@@ -78,9 +78,9 @@
     }
 
     /**
-     * Constructor from another entry.
+     * Copy constructor.
      *
-     * @param other the other entry to use.
+     * @param other the object to copy from.
      */
     public Port(Port other) {
 	this.value = other.value();
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 21da47e..33ba272 100644
--- a/src/main/java/net/onrc/onos/registry/controller/IControllerRegistryService.java
+++ b/src/main/java/net/onrc/onos/registry/controller/IControllerRegistryService.java
@@ -80,7 +80,7 @@
 	/**
 	 * Register a controller to the ONOS cluster. Must be called before
 	 * the registry can be used to take control of any switches.
-	 * @param controller A unique string ID identifying this controller
+	 * @param controllerId A unique string ID identifying this controller
 	 * in the cluster
 	 * @throws errors connecting to registry service, 
 	 * controllerId already registered
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 a9ff0b8..f552de5 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
@@ -1,5 +1,6 @@
 net.floodlightcontroller.core.FloodlightProvider
 net.floodlightcontroller.storage.memory.MemoryStorageSource
+net.onrc.onos.ofcontroller.floodlightlistener.NetworkGraphPublisher
 net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl
 net.onrc.onos.ofcontroller.linkdiscovery.internal.LinkDiscoveryManager
 net.floodlightcontroller.topology.TopologyManager
@@ -17,7 +18,6 @@
 net.floodlightcontroller.devicemanager.test.MockDeviceManager
 net.floodlightcontroller.core.test.MockFloodlightProvider
 net.floodlightcontroller.core.test.MockThreadPoolService
-net.onrc.onos.ofcontroller.floodlightlistener.NetworkGraphPublisher
 net.onrc.onos.ofcontroller.flowmanager.FlowManager
 net.onrc.onos.ofcontroller.routing.TopoRouteService
 net.onrc.onos.ofcontroller.bgproute.BgpRoute
diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
index ecdc2d7..4516ece 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
@@ -49,7 +49,7 @@
 import net.floodlightcontroller.core.IOFMessageListener;
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.IOFSwitchListener;
-import net.floodlightcontroller.core.internal.Controller.IUpdate;
+import net.floodlightcontroller.core.IUpdate;
 import net.floodlightcontroller.core.internal.Controller.SwitchUpdate;
 import net.floodlightcontroller.core.internal.Controller.SwitchUpdateType;
 import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState;
@@ -659,7 +659,7 @@
         controller.setRole(Role.SLAVE);
         verify(roleChanger);
         
-        Controller.IUpdate upd = controller.updates.poll();
+        IUpdate upd = controller.updates.poll();
         assertNotNull("Check that update queue has an update", upd);
         assertTrue("Check that update is HARoleUpdate", 
                    upd instanceof Controller.HARoleUpdate);
diff --git a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
index e83fc58..b37451a 100644
--- a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
+++ b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
@@ -38,6 +38,7 @@
 import net.floodlightcontroller.core.IOFSwitchFilter;
 import net.floodlightcontroller.core.IOFSwitchListener;
 import net.floodlightcontroller.core.IListener.Command;
+import net.floodlightcontroller.core.IUpdate;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
@@ -347,4 +348,10 @@
         // TODO Auto-generated method stub
         
     }
+
+	@Override
+	public void publishUpdate(IUpdate update) {
+		// TODO Auto-generated method stub
+		
+	}
 }
diff --git a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java
new file mode 100644
index 0000000..571d37d
--- /dev/null
+++ b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java
@@ -0,0 +1,204 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PatriciaTrieTest {
+
+	IPatriciaTrie<RibEntry> ptrie;
+	Prefix[] prefixes;
+	Map<Prefix, RibEntry> mappings;
+	
+	@Before
+	public void setUp() throws Exception {
+		ptrie = new PatriciaTrie<RibEntry>(32);
+		mappings = new HashMap<Prefix, RibEntry>();
+		
+		prefixes = new Prefix[] {
+			new Prefix("192.168.10.0", 24),
+			new Prefix("192.168.8.0", 23),
+			new Prefix("192.168.8.0", 22),
+			new Prefix("192.0.0.0", 7),
+			new Prefix("192.168.11.0", 24),
+			new Prefix("10.0.23.128", 25),
+			new Prefix("206.17.144.0", 20),
+			new Prefix("9.17.0.0", 12),
+			new Prefix("192.168.0.0", 16)
+		};
+				
+		for (int i = 0; i < prefixes.length; i++) {
+			mappings.put(prefixes[i], new RibEntry("192.168.10.101", "192.168.20." + i));
+			ptrie.put(prefixes[i], new RibEntry("192.168.10.101", "192.168.20." + i));
+		}
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+
+	@Test
+	public void testPut() {
+		IPatriciaTrie<RibEntry> ptrie = new PatriciaTrie<RibEntry>(32);
+		
+		Prefix p1 = new Prefix("192.168.240.0", 20);
+		RibEntry r1 = new RibEntry("192.168.10.101", "192.168.60.2");
+		RibEntry retval = ptrie.put(p1, r1);
+		assertNull(retval);
+		retval = ptrie.lookup(p1);
+		assertTrue(r1 == retval); //should be the same object
+		
+		Prefix p2 = new Prefix("192.160.0.0", 12);
+		RibEntry r2 = new RibEntry("192.168.10.101", "192.168.20.1");
+		retval = ptrie.put(p2, r2);
+		assertNull(retval);
+		
+		Prefix p3 = new Prefix("192.168.208.0", 20);
+		RibEntry r3 = new RibEntry("192.168.10.101", "192.168.30.1");
+		retval = ptrie.put(p3,  r3);
+		assertNull(retval);
+		
+		//Insert a new RibEntry entry over a previous one
+		RibEntry r3new = new RibEntry("192.168.10.101", "192.168.60.2");
+		retval = ptrie.put(p3, r3new);
+		assertNotNull(retval);
+		assertTrue(retval.equals(r3));
+		assertTrue(retval == r3); //should be the same object
+		
+		//Now we have an aggregate node with prefix 192.168.192.0/18.
+		//We will insert a RibEntry at this prefix
+		Prefix p4 = new Prefix("192.168.192.0", 18);
+		RibEntry r4 = new RibEntry("192.168.10.101", "192.168.40.1");
+		retval = ptrie.put(p4, r4);
+		assertNull(retval);
+		retval = ptrie.lookup(p4);
+		assertTrue(retval == r4); //should be the same object
+	}
+
+	@Test
+	public void testLookup() {
+		for (Map.Entry<Prefix, RibEntry> entry : mappings.entrySet()) {
+			RibEntry r = ptrie.lookup(entry.getKey());
+			assertTrue(entry.getValue().equals(r));
+		}
+		
+		//These are aggregate nodes in the tree. Shouldn't be returned by lookup
+		Prefix p1 = new Prefix("0.0.0.0", 0);
+		RibEntry retval = ptrie.lookup(p1);
+		assertNull(retval);
+		
+		//We'll put a RibEntry at an aggregate node and check if lookup returns correctly
+		Prefix p2 = new Prefix("192.0.0.0", 4);
+		RibEntry r2 = new RibEntry("192.168.10.101", "192.168.60.1");
+		retval = ptrie.put(p2, r2);
+		assertNull(retval);
+		retval = ptrie.lookup(p2);
+		assertTrue(retval.equals(r2));
+	}
+
+	//@Ignore
+	@Test
+	public void testMatch() {
+		Prefix p1 = new Prefix("192.168.10.30", 32);
+		Prefix p2 = new Prefix("192.168.10.30", 31);
+		Prefix p3 = new Prefix("192.168.8.241", 32);
+		Prefix p4 = new Prefix("1.0.0.0", 32);
+		Prefix p5 = new Prefix("192.168.8.0", 22);
+		Prefix p6 = new Prefix("192.168.8.0", 21);
+		
+		assertTrue(ptrie.match(p1).equals(mappings.get(prefixes[0])));
+		assertTrue(ptrie.match(p2).equals(mappings.get(prefixes[0])));
+		assertTrue(ptrie.match(p3).equals(mappings.get(prefixes[1])));
+		assertNull(ptrie.match(p4));
+		assertTrue(ptrie.match(p5).equals(mappings.get(prefixes[2])));
+		//System.out.println(ptrie.match(p6).getNextHop().getHostAddress());
+		assertTrue(ptrie.match(p6).equals(mappings.get(prefixes[8])));
+		
+		
+		//TODO more extensive tests
+		//fail("Not yet implemented");
+	}
+
+	@Test
+	public void testRemove() {
+		Prefix p1 = new Prefix("192.168.8.0", 23);
+		RibEntry retval = ptrie.lookup(p1);
+		assertNotNull(retval);
+		boolean success = ptrie.remove(p1, retval);
+		assertTrue(success);
+		
+		Prefix p2 = new Prefix("192.168.8.0", 22);
+		Prefix p3 = new Prefix("192.168.10.0", 24);
+		
+		//Test it does the right thing with null arguments
+		success = ptrie.remove(null, null);
+		assertFalse(success);
+		success = ptrie.remove(p2, null);
+		assertFalse(success);
+		
+		//Check other prefixes are still there
+		retval = ptrie.lookup(p2);
+		assertNotNull(retval);
+		retval = ptrie.lookup(p3);
+		assertNotNull(retval);
+		
+		Prefix p4 = new Prefix("9.17.0.0", 12);
+		retval = ptrie.lookup(p4);
+		assertNotNull(retval);
+		success = ptrie.remove(p4, retval);
+		assertTrue(success);
+		success = ptrie.remove(p4, retval);
+		assertFalse(success);
+		
+		//Check other prefixes are still there
+		retval = ptrie.lookup(p2);
+		assertNotNull(retval);
+		retval = ptrie.lookup(p3);
+		assertNotNull(retval);
+		
+		Prefix p5 = new Prefix("192.0.0.0", 7);
+		retval = ptrie.lookup(p5);
+		assertNotNull(retval);
+		success = ptrie.remove(p5, retval);
+		assertTrue(success);
+		
+		//Check other prefixes are still there
+		retval = ptrie.lookup(p2);
+		assertNotNull(retval);
+		retval = ptrie.lookup(p3);
+		assertNotNull(retval);
+		
+		
+	}
+
+	@Test(expected=java.util.NoSuchElementException.class)
+	public void testIterator() {		
+		int[] order = new int[] {7, 5, 3, 8, 2, 1, 0, 4, 6};
+		
+		Iterator<IPatriciaTrie.Entry<RibEntry>> it = ptrie.iterator();
+		int i = 0;
+		assertTrue(it.hasNext());
+		while (it.hasNext()) {
+			IPatriciaTrie.Entry<RibEntry> entry = it.next();
+			assertTrue(entry.getPrefix().equals(prefixes[order[i]]));
+			i++;
+		}
+		assertFalse(it.hasNext());
+		assertTrue(i == order.length);
+		
+		IPatriciaTrie<RibEntry> pt = new PatriciaTrie<RibEntry>(32);
+		Iterator<IPatriciaTrie.Entry<RibEntry>> it2 = pt.iterator();
+		assertFalse(it2.hasNext());
+		it.next(); //throws NoSuchElementException
+	}
+
+}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PrefixTest.java b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PrefixTest.java
new file mode 100644
index 0000000..6ebaf0f
--- /dev/null
+++ b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PrefixTest.java
@@ -0,0 +1,101 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PrefixTest {
+
+	@Before
+	public void setUp() throws Exception {
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+
+	@Test
+	public void testPrefixByteArray() {
+		byte[] b1 = new byte[] {(byte)0x8f, (byte)0xa0, (byte)0x00, (byte)0x00};
+		byte[] b2 = new byte[] {(byte)0x8f, (byte)0xa0, (byte)0xff, (byte)0xff};
+		byte[] b3 = new byte[] {(byte)0x8f, (byte)0xac, (byte)0x00, (byte)0x00};
+		byte[] b4 = new byte[] {(byte)0x8f, (byte)0xa0, (byte)0x00, (byte)0x00};
+		
+		Prefix p1 = new Prefix(b1, 12);
+		Prefix p2 = new Prefix(b2, 12);
+		Prefix p3 = new Prefix(b3, 12);
+		Prefix p4 = new Prefix(b4, 11);
+		
+		//Have different byte arrays, but should be equal after construction
+		assertTrue(p1.equals(p2));
+		assertTrue(p2.equals(p3));
+		
+		//Same byte array, but should be false
+		assertFalse(p1.equals(p4));
+		
+		assertTrue(Arrays.equals(p1.getAddress(), p3.getAddress()));
+		assertTrue(p1.toString().equals(p2.toString()));
+		assertTrue(Arrays.equals(p1.getAddress(), p4.getAddress()));
+		assertFalse(p1.toString().equals(p4.toString()));
+	}
+
+	@Test
+	public void testPrefixString() {
+		Prefix p1 = new Prefix("192.168.166.0", 24);
+		Prefix p2 = new Prefix("192.168.166.0", 23);
+		Prefix p3 = new Prefix("192.168.166.128", 24);
+		Prefix p4 = new Prefix("192.168.166.128", 25);
+		
+		assertFalse(p1.equals(p2));
+		assertTrue(Arrays.equals(p1.getAddress(), p2.getAddress()));
+		
+		assertTrue(p1.equals(p3));
+		assertTrue(Arrays.equals(p1.getAddress(), p2.getAddress()));
+		
+		assertFalse(p3.equals(p4));
+		assertFalse(Arrays.equals(p3.getAddress(), p4.getAddress()));
+		
+		assertTrue(p1.toString().equals(p3.toString()));
+		assertEquals(p1.hashCode(), p3.hashCode());
+	}
+
+	@Test
+	public void testPrefixReturnsSame() {
+		//Create a prefix of all 1s for each prefix length.
+		//Check that Prefix doesn't mangle it
+		int MAX_PREFIX_LENGTH = 32;
+		for (int prefixLength = 1; prefixLength <= MAX_PREFIX_LENGTH; prefixLength++) {
+			byte address[] = new byte[MAX_PREFIX_LENGTH/Byte.SIZE];
+			
+			int lastByte = (prefixLength - 1) / Byte.SIZE;
+			int lastBit = (prefixLength - 1) % Byte.SIZE;
+			
+			for (int j = 0; j < address.length; j++) {
+				if (j < lastByte) {
+					address[j] = (byte)0xff;
+				}
+				else if (j == lastByte) {
+					byte b = 0;
+					byte msb = (byte) 0x80;
+					for (int k = 0; k < Byte.SIZE; k++) {
+						if (k <= lastBit) {
+							b |= (msb >> k);
+						}
+					}
+					address[j] = b;
+				}
+				else {
+					address[j] = 0;
+				}
+			}
+			
+			Prefix p = new Prefix(address, prefixLength);
+			System.out.println(p.printAsBits());
+			assertTrue(Arrays.equals(address, p.getAddress()));
+		}
+	}
+}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java
new file mode 100644
index 0000000..1135407
--- /dev/null
+++ b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java
@@ -0,0 +1,228 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.net.InetAddresses;
+
+public class PtreeTest {
+	
+	private Logger log = LoggerFactory.getLogger(PtreeTest.class);
+	
+	private Ptree ptree;
+	private PatriciaTrie<RibEntry> ooptrie;
+	
+	private Map<String, byte[]> byteAddresses;
+
+	@Before
+	public void setUp() throws Exception {
+		ptree = new Ptree(32);
+		ooptrie = new PatriciaTrie<RibEntry>(32);
+			
+		String[] strPrefixes = {
+			"192.168.10.0/24",
+			"192.168.10.0/23",
+			"192.168.10.0/22",
+			"192.0.0.0/7",
+			"192.168.11.0/24",
+			"10.0.23.128/25",
+			"206.17.144.0/20",
+			"9.17.0.0/12",
+			"192.168.0.0/16"
+		};
+		
+		byteAddresses = new HashMap<String, byte[]>(strPrefixes.length+10);
+		for (String prefix : strPrefixes) {
+			String address = prefix.split("/")[0];
+			int prefixLength = Integer.parseInt(prefix.split("/")[1]);
+			byteAddresses.put(prefix, InetAddresses.forString(address).getAddress());
+			
+			PtreeNode node = ptree.acquire(byteAddresses.get(prefix), prefixLength);
+			node.rib = new RibEntry("192.168.10.101", "192.168.60.1");
+			ooptrie.put(new Prefix(byteAddresses.get(prefix), prefixLength), 
+					new RibEntry("192.168.10.101", "192.168.60.1"));
+		}
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+
+	@Ignore
+	@Test
+	public void testAcquireByteArray() {
+		fail("Not yet implemented");
+	}
+
+	@Ignore
+	@Test
+	public void testAcquireByteArrayInt() {
+		//First let's test an empty Ptree
+		Ptree localPtree = new Ptree(32);
+		PtreeNode node = localPtree.acquire(new byte[] {0x00, 0x00, 0x00, 0x00});
+		assertTrue(node != null && node.rib == null);
+		
+		//Now let's look at the prepopulated tree
+		String testPrefix = "206.17.144.0/20";
+		PtreeNode existingNode = ptree.acquire(byteAddresses.get(testPrefix), 20);
+		printByteArray(existingNode.key);
+		printByteArray(byteAddresses.get(testPrefix));
+		assertTrue(existingNode != null && existingNode.rib == null);
+		
+		assertTrue(Arrays.equals(existingNode.key, byteAddresses.get(testPrefix)));
+	}
+
+	@Test
+	public void testLookup() {
+		String prefix1 = "192.168.10.12";
+		int length1 = 29;
+		PtreeNode node1 = ptree.lookup(InetAddresses.forString(prefix1).getAddress(), length1);
+		
+		//There should be no direct match
+		assertTrue(node1 == null);
+		
+		log.debug("{} null: {}", "node1", node1 == null ? "true" : "false");
+		
+		String prefix2 = "206.17.144.0";
+		int length2 = 20;
+		PtreeNode node2 = ptree.lookup(InetAddresses.forString(prefix2).getAddress(), length2);
+		
+		assertTrue(node2 != null);
+		assertTrue(Arrays.equals(node2.key, byteAddresses.get(prefix2 + "/" + length2)));
+		
+		log.debug("{} null: {}", "node2", node2 == null ? "true" : "false");
+		if (node2 != null) {
+			log.debug("{} key: {}, keybits: {}", new Object[] {"node2", node2.key, node2.keyBits});
+		}
+		
+		String prefix3 = "192.0.0.0";
+		int length3 = 7;
+		PtreeNode node3 = ptree.lookup(InetAddresses.forString(prefix3).getAddress(), length3);
+		assertTrue(node3 != null);
+	}
+
+	@Test
+	public void testMatch() {
+		String prefix1 = "192.168.10.12";
+		int length1 = 29;
+		PtreeNode node1 = ptree.match(InetAddresses.forString(prefix1).getAddress(), length1);
+		
+		//There should be no direct match, but we should get the covering prefix
+		assertTrue(node1 != null);
+		assertTrue(Arrays.equals(node1.key, byteAddresses.get("192.168.10.0/24")));
+		
+		log.debug("{} null: {}", "node1", node1 == null ? "true" : "false");
+		if (node1 != null) {
+			log.debug("{} key: {}, keybits: {}", new Object[] {"node1", node1.key, node1.keyBits});
+		}
+	}
+	
+	@Ignore
+	@Test
+	public void testTraverse() {
+		
+		String expected = "[0, 0, 0, 0]/0\n";
+		expected += "[8, 0, 0, 0]/6\n";
+		expected += "[9, 17, 0, 0]/12\n";
+		expected += "[10, 0, 23, -128]/25\n";
+		expected += "[-64, 0, 0, 0]/4\n";
+		expected += "[-64, -88, 0, 0]/16\n";
+		expected += "[-64, -88, 8, 0]/22\n";
+		expected += "[-64, -88, 10, 0]/23\n";
+		expected += "[-64, -88, 10, 0]/24\n";
+		expected += "[-64, -88, 11, 0]/24\n";
+		expected += "[-50, 17, -112, 0]/20\n";
+		
+		PtreeNode node;
+		String result = "";
+		
+		for (node = ptree.begin(); node != null; node = ptree.next(node)) {
+			result += printByteArray(node.key) + "/" + node.keyBits + "\n";
+		}
+		
+		assertEquals(expected, result);
+	}
+
+	@Ignore
+	@Test
+	public void testBegin() {
+		fail("Not yet implemented");
+	}
+
+	@Ignore
+	@Test
+	public void testNext() {
+		fail("Not yet implemented");
+	}
+
+	@Ignore
+	@Test
+	public void testDelReference() {
+		fail("Not yet implemented");
+	}
+	
+	@Ignore
+	@Test
+	public void testMisc() {
+		int bitIndex = -1;
+		int index = (int)(bitIndex / Byte.SIZE);
+	    int bit = (int)(bitIndex % Byte.SIZE);
+	    
+	    log.debug("index {} bit {}", index, bit); 
+	    log.debug("percent {}", 1%8);
+	    
+	    //PtreeNode node1 = new PtreeNode(new byte[] {0x0, 0x0, 0x0, 0x0}, 0, 4);
+	    PtreeNode node1 = new PtreeNode(null, 0, 4);
+	    log.debug("node1: key {}, keybits {}", printByteArray(node1.key), node1.keyBits);
+	    
+	    //PtreeNode node2 = new PtreeNode(new byte[] {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff}, 
+	    PtreeNode node2 = new PtreeNode(null,
+	    		32, 4);
+	    log.debug("node2: key {}, keybits {}", printByteArray(node2.key), node2.keyBits);
+	}
+	
+	@Test
+	public void testIteration() {
+		Iterator<IPatriciaTrie.Entry<RibEntry>> it = ooptrie.iterator();
+		
+		while (it.hasNext()) {
+			IPatriciaTrie.Entry<RibEntry> entry = it.next();
+			log.debug("PatriciaTrie prefix {} \t {}", entry.getPrefix(), entry.getPrefix().printAsBits());
+		}
+		
+		try {
+			PtreeNode node;
+			for (node = ptree.begin(); node != null; node = ptree.next(node)) {
+				log.debug("Ptree prefix {}/{}", InetAddress.getByAddress(node.key).getHostAddress(), node.keyBits);
+			}
+		} catch (UnknownHostException e) {
+			
+		}
+	}
+	
+	private String printByteArray(byte[] array) {
+		String result = "[";
+		for (byte b : array) {
+			result += b + ", ";
+		}
+		result = result.substring(0, result.length() - 2);
+		result += "]";
+		return result;
+	}
+
+}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIDeviceObjectTest.java b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIDeviceObjectTest.java
index 3ea90ba..323a0eb 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIDeviceObjectTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIDeviceObjectTest.java
@@ -7,8 +7,6 @@
 import net.onrc.onos.graph.GraphDBConnection;
 import net.onrc.onos.graph.GraphDBOperation;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
-import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowEntry;
-import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IFlowPath;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
 import net.onrc.onos.ofcontroller.core.internal.SwitchStorageImpl;
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowEntryTest.java b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowEntryTest.java
index 717d688..06d8522 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowEntryTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowEntryTest.java
@@ -353,18 +353,18 @@
 	
 	/**
 	 * Desc:
-	 *  Test method for set and get ActionOutput.
+	 *  Test method for set and get ActionOutputPort.
 	 * Condition:
 	 *  N/A
 	 * Expect:
-	 * 1. Should set ActionOutput.
-	 * 2. Should get ActionOutput.
+	 * 1. Should set ActionOutputPort.
+	 * 2. Should get ActionOutputPort.
 	 */
 	@Test
-	public void testSetGetActionOutput() {
-		Short actionOutput = 1;
-		flowEntry.setActionOutput(actionOutput);
-		assertEquals(flowEntry.getActionOutput(), actionOutput);
+	public void testSetGetActionOutputPort() {
+		Short actionOutputPort = 1;
+		flowEntry.setActionOutputPort(actionOutputPort);
+		assertEquals(flowEntry.getActionOutputPort(), actionOutputPort);
 	}
 	
 	/**
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java
index 85072e3..bb0dbbf 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowPathTest.java
@@ -104,6 +104,24 @@
 
 	/**
 	 * Desc:
+	 *  Test method for get and set FlowPathFlags method.
+	 * Condition:
+	 *  N/A
+	 * Expect:
+	 * 1. Should set the Flow Path Flags.
+	 * 2. Should get the Flow Path Flags.
+	 */
+	@Test
+	public void testSetGetFlowPathFlags() {
+		String flowId = "xx";
+		Long flowPathFlags = new Long(0x3);
+		flowPath.setFlowId(flowId);
+		flowPath.setFlowPathFlags(flowPathFlags);
+		assertEquals(flowPath.getFlowPathFlags(), flowPathFlags);
+	}
+
+	/**
+	 * Desc:
 	 *  Test method for get and set SourceSwitch method.
 	 * Condition:
 	 *  N/A
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIPortObjectTest.java b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIPortObjectTest.java
index 2ddab3d..74f9758 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIPortObjectTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIPortObjectTest.java
@@ -11,10 +11,6 @@
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
 import net.onrc.onos.ofcontroller.core.internal.SwitchStorageImpl;
 import net.onrc.onos.ofcontroller.core.internal.TestDatabaseManager;
-import net.onrc.onos.ofcontroller.flowmanager.FlowManager;
-import net.onrc.onos.ofcontroller.util.FlowId;
-import net.onrc.onos.ofcontroller.util.FlowPath;
-
 import org.easymock.EasyMock;
 import org.junit.After;
 import org.junit.Before;
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImplTest.java b/src/test/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImplTest.java
index 4956136..f276680 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImplTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImplTest.java
@@ -13,7 +13,6 @@
 import org.easymock.EasyMock;
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.openflow.protocol.OFPhysicalPort;
@@ -469,7 +468,8 @@
 	public void testAddPortWithPortLinkDown() {
 		String dpid = "00:00:00:00:00:00:0a:01";
 		short portNumber = 5;
-		String state = "ACTIVE";
+		String swState = "ACTIVE";
+		String portState = "INACTIVE";
 		String name = "port 5 at SEA switch";
 		
 		OFPhysicalPort portToAdd = new OFPhysicalPort();
@@ -480,15 +480,15 @@
 		
 		//Expectation of  mock Port
 		IPortObject mockIPort = createMock(IPortObject.class);
-		mockIPort.setState(state);
+		mockIPort.setState(portState);
 		mockIPort.setPortState(OFPortState.OFPPS_STP_FORWARD.getValue());
 		mockIPort.setDesc(name);
 		replay(mockIPort);
 		
 		//Expectation of mock Switch
 		ISwitchObject mockISw = createMock(ISwitchObject.class);
-		mockISw.setState(state);
-		mockISw.removePort(mockIPort);
+		mockISw.setState(swState);
+//		mockISw.removePort(mockIPort);
 		replay(mockISw);
 		
 		//Expectation of mock operation.
@@ -497,7 +497,7 @@
 		mockOpe.commit();	
 		expect(mockOpe.searchSwitch(dpid)).andReturn(mockISw);
 		expect(mockOpe.searchPort(dpid, portNumber)).andReturn(mockIPort);
-		mockOpe.removePort(mockIPort);
+//		mockOpe.removePort(mockIPort);
 		mockOpe.commit();
 		mockOpe.close();
 		replay(mockOpe);
@@ -663,7 +663,8 @@
 	public void testDeletePort() {
 		String dpid = "00:00:00:00:00:00:0a:01";
 		short portNumber = 5;
-		String state = "ACTIVE";
+		String portState = "INACTIVE";
+		String swState = "ACTIVE";
 		String name = "port 5 at SEA switch";
 		
 		OFPhysicalPort portToAdd = new OFPhysicalPort();
@@ -674,18 +675,21 @@
 		
 		//Expectation of  mock Port
 		IPortObject mockIPort = createMock(IPortObject.class);
-		mockIPort.setState(state);
+		mockIPort.setState(swState);
 		mockIPort.setPortState(OFPortState.OFPPS_STP_FORWARD.getValue());
 		mockIPort.setDesc(name);
+		mockIPort.setState(portState);
 		replay(mockIPort);
 		
 		//Expectation of mock Switch
 		ISwitchObject mockISw = createMock(ISwitchObject.class);
-		mockISw.setState(state);
+		mockISw.setState(swState);
 		mockISw.addPort(mockIPort);
-		mockISw.removePort(mockIPort);
+//		mockISw.removePort(mockIPort);
 		replay(mockISw);
 		
+		
+
 		//Expectation of mock operation.
 		expect(mockOpe.searchSwitch(dpid)).andReturn(null);
 		expect(mockOpe.newSwitch(dpid)).andReturn(mockISw);
@@ -696,7 +700,7 @@
 		mockOpe.commit();
 		expect(mockOpe.searchSwitch(dpid)).andReturn(mockISw);
 		expect(mockOpe.searchPort(dpid, portNumber)).andReturn(mockIPort);
-		mockOpe.removePort(mockIPort);
+//		mockOpe.removePort(mockIPort);
 		mockOpe.commit();
 		mockOpe.close();
 		replay(mockOpe);
@@ -720,7 +724,8 @@
 	public void testDeletePortException() {
 		String dpid = "00:00:00:00:00:00:0a:01";
 		short portNumber = 5;
-		String state = "ACTIVE";
+		String swState = "ACTIVE";
+		String portState = "INACTIVE";
 		String name = "port 5 at SEA switch";
 		
 		OFPhysicalPort portToAdd = new OFPhysicalPort();
@@ -731,16 +736,17 @@
 		
 		//Expectation of  mock Port
 		IPortObject mockIPort = createMock(IPortObject.class);
-		mockIPort.setState(state);
+		mockIPort.setState(swState);
 		mockIPort.setPortState(OFPortState.OFPPS_STP_FORWARD.getValue());
 		mockIPort.setDesc(name);
+		mockIPort.setState(portState);
 		replay(mockIPort);
 		
 		//Expectation of mock Switch
 		ISwitchObject mockISw = createMock(ISwitchObject.class);
-		mockISw.setState(state);
+		mockISw.setState(swState);
 		mockISw.addPort(mockIPort);
-		mockISw.removePort(mockIPort);
+//		mockISw.removePort(mockIPort);
 		replay(mockISw);
 		
 		//Expectation of mock operation.
@@ -753,9 +759,10 @@
 		mockOpe.commit();
 		expect(mockOpe.searchSwitch(dpid)).andReturn(mockISw);
 		expect(mockOpe.searchPort(dpid, portNumber)).andReturn(mockIPort);
-		mockOpe.removePort(mockIPort);
-		expectLastCall().andThrow(new RuntimeException());
-		mockOpe.rollback();
+		mockOpe.commit(); // Cannot generate exception..need to revisit this test
+//		mockOpe.removePort(mockIPort);
+//		expectLastCall().andThrow(new RuntimeException());
+//		mockOpe.rollback();
 		mockOpe.close();
 		replay(mockOpe);
 	
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImplTestBB.java b/src/test/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImplTestBB.java
index 57aefd3..19ac709 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImplTestBB.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImplTestBB.java
@@ -15,7 +15,6 @@
 import org.easymock.EasyMock;
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.openflow.protocol.OFPhysicalPort;
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
index fb5227f..bcff36a 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
@@ -415,6 +415,7 @@
 	
 	public static class TestFlowPath implements IFlowPath {
 		private String state,type,flowId,installerId,srcSw,dstSw;
+		private Long flowPathFlags;
 		private String dataPathSummary,userState;
 		private Short srcPort,dstPort;
 		private String matchSrcMac,matchDstMac;
@@ -424,11 +425,13 @@
 		private String matchSrcIpaddr,matchDstIpaddr;
 		private Byte matchIpProto, matchIpToS;
 		private Short matchSrcTcpUdpPort, matchDstTcpUdpPort;
+		private String actions;
 		
 		private List<IFlowEntry> entries;
 		private List<ISwitchObject> switches;
 
 		private String stateToUpdate,typeToUpdate,flowIdToUpdate,installerIdToUpdate,srcSwToUpdate,dstSwToUpdate;
+		private Long flowPathFlagsToUpdate;
 		private String dataPathSummaryToUpdate,userStateToUpdate;
 		private Short srcPortToUpdate,dstPortToUpdate;
 		private String matchSrcMacToUpdate,matchDstMacToUpdate;
@@ -438,6 +441,7 @@
 		private String matchSrcIpaddrToUpdate,matchDstIpaddrToUpdate;
 		private Byte matchIpProtoToUpdate, matchIpToSToUpdate;
 		private Short matchSrcTcpUdpPortToUpdate, matchDstTcpUdpPortToUpdate;
+		private String actionsToUpdate;
 
 		private List<IFlowEntry> flowsToAdd;
 		private List<IFlowEntry> flowsToRemove;
@@ -465,6 +469,7 @@
 			if(typeToUpdate != null) { type = typeToUpdate; }
 			if(flowIdToUpdate != null) { flowId = flowIdToUpdate; }
 			if(installerIdToUpdate != null) { installerId = installerIdToUpdate; }
+			if(flowPathFlagsToUpdate != null) { flowPathFlags = flowPathFlagsToUpdate; }
 			if(srcSwToUpdate != null) { srcSw = srcSwToUpdate; }
 			if(dstSwToUpdate != null) { dstSw = dstSwToUpdate; }
 			if(dataPathSummaryToUpdate != null) { dataPathSummary = dataPathSummaryToUpdate; }
@@ -482,6 +487,7 @@
 			if(matchIpToSToUpdate != null) { matchIpToS = matchIpToSToUpdate; }
 			if(matchSrcTcpUdpPortToUpdate != null) { matchSrcTcpUdpPort = matchSrcTcpUdpPortToUpdate; }
 			if(matchDstTcpUdpPortToUpdate != null) { matchDstTcpUdpPort = matchDstTcpUdpPortToUpdate; }
+			if(actionsToUpdate != null) { actions = actionsToUpdate; }
 		}
 		
 		public void rollback() {
@@ -493,6 +499,7 @@
 			flowsToRemove.clear();
 			
 			stateToUpdate = typeToUpdate = flowIdToUpdate = installerIdToUpdate = null;
+			flowPathFlagsToUpdate = null;
 			srcSwToUpdate = dstSwToUpdate = dataPathSummaryToUpdate = userStateToUpdate = null;
 			srcPortToUpdate = dstPortToUpdate = null;
 			matchSrcMacToUpdate = matchDstMacToUpdate = null;
@@ -502,6 +509,7 @@
 			matchSrcIpaddrToUpdate = matchDstIpaddrToUpdate = null;
 			matchIpProtoToUpdate = matchIpToSToUpdate = null;
 			matchSrcTcpUdpPortToUpdate = matchDstTcpUdpPortToUpdate = null;
+			actionsToUpdate = null;
 		}
 		
 		// Setter methods for test
@@ -509,6 +517,7 @@
 		public void setTypeForTest(String type) { this.type = type; }
 		public void setFlowIdForTest(String flowId) { this.flowId = flowId; }
 		public void setInstallerIdForTest(String installerId) { this.installerId = installerId; }
+		public void setFlowPathFlagsForTest(Long flowPathFlags) { this.flowPathFlags = flowPathFlags; }
 		public void setSrcSwForTest(String srcSw) { this.srcSw = srcSw; }
 		public void setDstSwForTest(String dstSw) { this.dstSw = dstSw; }
 		public void setDataPathSummaryForTest(String dataPathSummary) { this.dataPathSummary = dataPathSummary; }
@@ -526,6 +535,7 @@
 		public void setMatchIpToSForTest(Byte matchIpToS) { this.matchIpToS = matchIpToS; }
 		public void setMatchSrcTcpUdpPortForTest(Short matchSrcTcpUdpPort) { this.matchSrcTcpUdpPort = matchSrcTcpUdpPort; }
 		public void setMatchDstTcpUdpPortForTest(Short matchDstTcpUdpPort) { this.matchDstTcpUdpPort = matchDstTcpUdpPort; }
+		public void setActionsForTest(String actions) { this.actions = actions; }
 		public void addFlowEntryForTest(IFlowEntry entry) { entries.add(entry); }
 		public void addSwitchForTest(ISwitchObject sw) { switches.add(sw); }
 
@@ -560,6 +570,12 @@
 		public void setInstallerId(String installerId) { installerIdToUpdate = installerId; }
 
 		@Override
+		public Long getFlowPathFlags() { return flowPathFlags; }
+
+		@Override
+		public void setFlowPathFlags(Long flowPathFlags) { flowPathFlagsToUpdate = flowPathFlags; }
+
+		@Override
 		public String getSrcSwitch() { return srcSw; }
 
 		@Override
@@ -682,6 +698,13 @@
 			matchDstTcpUdpPortToUpdate = matchDstTcpUdpPort; }
 
 		@Override
+		public String getActions() { return actions; }
+
+		@Override
+		public void setActions(String actions) {
+			actionsToUpdate = actions; }
+
+		@Override
 		public Iterable<ISwitchObject> getSwitches() { return switches; }
 
 		@Override
@@ -701,7 +724,8 @@
 		private String matchSrcIpaddr,matchDstIpaddr;
 		private Byte matchIpProto, matchIpToS;
 		private Short matchSrcTcpUdpPort, matchDstTcpUdpPort;
-		private Short actionOutput;
+		private Short actionOutputPort;
+		private String actions;
 		
 		private IFlowPath flowPath;
 		private ISwitchObject sw;
@@ -717,7 +741,8 @@
 		private String matchSrcIpaddrToUpdate,matchDstIpaddrToUpdate;
 		private Byte matchIpProtoToUpdate, matchIpToSToUpdate;
 		private Short matchSrcTcpUdpPortToUpdate, matchDstTcpUdpPortToUpdate;
-		private Short actionOutputToUpdate;
+		private Short actionOutputPortToUpdate;
+		private String actionsToUpdate;
 	
 		private IFlowPath flowPathToUpdate;
 		private ISwitchObject swToUpdate;
@@ -750,7 +775,8 @@
 			if(matchIpToSToUpdate != null) { matchIpToS = matchIpToSToUpdate; }
 			if(matchSrcTcpUdpPortToUpdate != null) { matchSrcTcpUdpPort = matchSrcTcpUdpPortToUpdate; }
 			if(matchDstTcpUdpPortToUpdate != null) { matchDstTcpUdpPort = matchDstTcpUdpPortToUpdate; }
-			if(actionOutputToUpdate != null) { actionOutput = actionOutputToUpdate; }
+			if(actionOutputPortToUpdate != null) { actionOutputPort = actionOutputPortToUpdate; }
+			if(actionsToUpdate != null) { actions = actionsToUpdate; }
 			
 			if(flowPathToUpdate != null) { flowPath = flowPathToUpdate; }
 			if(swToUpdate != null) { sw = swToUpdate; }
@@ -775,7 +801,8 @@
 			matchSrcIpaddrToUpdate = matchDstIpaddrToUpdate = null;
 			matchIpProtoToUpdate = matchIpToSToUpdate = null;
 			matchSrcTcpUdpPortToUpdate = matchDstTcpUdpPortToUpdate = null;
-			actionOutputToUpdate = null;
+			actionOutputPortToUpdate = null;
+			actionsToUpdate = null;
 			flowPathToUpdate = null;
 			swToUpdate = null;
 			inportToUpdate = outportToUpdate = null;
@@ -802,7 +829,8 @@
 		public void setMatchIpToSForTest(Byte matchIpToS) { this.matchIpToS = matchIpToS; }
 		public void setMatchSrcTcpUdpPortForTest(Short matchSrcTcpUdpPort) { this.matchSrcTcpUdpPort = matchSrcTcpUdpPort; }
 		public void setMatchDstTcpUdpPortForTest(Short matchDstTcpUdpPort) { this.matchDstTcpUdpPort = matchDstTcpUdpPort; }
-		public void setActionOutputForTest(Short actionOutput) { this.actionOutput = actionOutput; }
+		public void setActionOutputPortForTest(Short actionOutputPort) { this.actionOutputPort = actionOutputPort; }
+		public void setActionsForTest(String actions) { this.actions = actions; }
 		public void setFlowPathForTest(IFlowPath flowPath) { this.flowPath = flowPath; }
 		public void setSwitchForTest(ISwitchObject sw) { this.sw = sw; }
 		public void setInportForTest(IPortObject inport) { this.inport = inport; }
@@ -935,10 +963,16 @@
 		public void setMatchDstTcpUdpPort(Short matchDstTcpUdpPort) { matchDstTcpUdpPortToUpdate = matchDstTcpUdpPort; }
 	
 		@Override
-		public Short getActionOutput() { return actionOutput; }
+		public Short getActionOutputPort() { return actionOutputPort; }
 	
 		@Override
-		public void setActionOutput(Short actionOutput) { actionOutputToUpdate = actionOutput; }
+		public void setActionOutputPort(Short actionOutputPort) { actionOutputPortToUpdate = actionOutputPort; }
+
+		@Override
+		public String getActions() { return actions; }
+	
+		@Override
+		public void setActions(String actions) { actionsToUpdate = actions; }
 	
 		@Override
 		public IFlowPath getFlow() { return flowPath; }
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableSwitchStorageImpl.java b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableSwitchStorageImpl.java
index e78a1b6..e0b34e1 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableSwitchStorageImpl.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableSwitchStorageImpl.java
@@ -1,13 +1,7 @@
 package net.onrc.onos.ofcontroller.core.internal;
 
-import java.util.Set;
-
 import net.onrc.onos.ofcontroller.core.internal.SwitchStorageImpl;
 
-import com.thinkaurelius.titan.core.TitanGraph;
-import com.tinkerpop.blueprints.Vertex;
-import com.tinkerpop.blueprints.TransactionalGraph.Conclusion;
-
 /**
  * Seam that allows me to set up a testable instance of SwitchStorageImpl that 
  * writes to a file database rather than a Cassandra cluster. 
diff --git a/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java b/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
index 750bfba..83a5fab 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
@@ -80,10 +80,12 @@
 	}
 	
 	private IFlowPath createIFlowPathMock(long flowId, String installerID,
+			long flowPathFlags,
 			long srcDpid, int srcPort, long dstDpid, int dstPort) {
 		IFlowPath iFlowPath = createNiceMock(IFlowPath.class);
 		expect(iFlowPath.getFlowId()).andReturn(new FlowId(flowId).toString()).anyTimes();
 		expect(iFlowPath.getInstallerId()).andReturn(installerID).anyTimes();
+		expect(iFlowPath.getFlowPathFlags()).andReturn(new Long(flowPathFlags)).anyTimes();
 		expect(iFlowPath.getSrcSwitch()).andReturn(new Dpid(srcDpid).toString()).anyTimes();
 		expect(iFlowPath.getSrcPort()).andReturn(new Short((short)srcPort)).anyTimes();
 		expect(iFlowPath.getDstSwitch()).andReturn(new Dpid(dstDpid).toString()).anyTimes();
@@ -92,12 +94,14 @@
 	}
 	
 	private FlowPath createTestFlowPath(long flowId, String installerId,
+			final long flowPathFlags,
 			final long srcDpid, final int srcPort,
 			final long dstDpid, final int dstPort			
 			) {
 		FlowPath flowPath = new FlowPath();
 		flowPath.setFlowId(new FlowId(flowId));
 		flowPath.setInstallerId(new CallerId(installerId));
+		flowPath.setFlowPathFlags(new FlowPathFlags(flowPathFlags));
 		flowPath.setDataPath(new DataPath() {{
 			setSrcPort(new SwitchPort(new Dpid(srcDpid), new Port((short)srcPort)));
 			setDstPort(new SwitchPort(new Dpid(dstDpid), new Port((short)dstPort)));
@@ -107,9 +111,9 @@
 	}
 	
 	private ArrayList<FlowPath> createTestFlowPaths() {
-		FlowPath flowPath1 = createTestFlowPath(1, "foo caller id", 1, 1, 2, 2); 
-		FlowPath flowPath2 = createTestFlowPath(2, "caller id", 1, 1, 2, 2); 
-		FlowPath flowPath3 = createTestFlowPath(3, "caller id", 1, 5, 2, 2); 
+		FlowPath flowPath1 = createTestFlowPath(1, "foo caller id", 0, 1, 1, 2, 2); 
+		FlowPath flowPath2 = createTestFlowPath(2, "caller id", 0, 1, 1, 2, 2); 
+		FlowPath flowPath3 = createTestFlowPath(3, "caller id", 0, 1, 5, 2, 2); 
 
 		ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
 		flowPaths.add(flowPath1);
@@ -182,6 +186,7 @@
 		FlowPath flowPath = new FlowPath();
 		flowPath.setFlowId(new FlowId(0x100));
 		flowPath.setInstallerId(new CallerId("installer id"));
+		flowPath.setFlowPathFlags(new FlowPathFlags(0));
 		flowPath.setDataPath(dataPath);
 		flowPath.setFlowEntryMatch(match);
 		
@@ -192,6 +197,7 @@
 		createdFlowPath.setFlowId("0x100");
 		createdFlowPath.setType("flow");
 		createdFlowPath.setInstallerId("installer id");
+		createdFlowPath.setFlowPathFlags(new Long((long)0));
 		createdFlowPath.setSrcSwitch("00:00:00:00:00:00:12:34");
 		createdFlowPath.setSrcPort(new Short((short)1));
 		createdFlowPath.setDstSwitch("00:00:00:00:00:00:56:78");
@@ -339,7 +345,7 @@
 	public final void testGetFlowSuccessNormally() throws Exception {
 		// instantiate required objects
 		FlowManager fm = new FlowManager();
-		IFlowPath iFlowPath = createIFlowPathMock(1, "caller id", 1, 1, 2, 2); 
+		IFlowPath iFlowPath = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2); 
 
 		// setup expectations
 		expectInitWithContext();
@@ -351,11 +357,14 @@
 		replayAll();
 
 		fm.init(context);
-		String result = fm.getFlow(new FlowId(1)).installerId().toString();
+		FlowPath flowPath = fm.getFlow(new FlowId(1));
+		String installerId = flowPath.installerId().toString();
+		long flowPathFlags = flowPath.flowPathFlags().flags();
 		
 		//verify the test
 		verifyAll();
-		assertEquals("caller id", result);
+		assertEquals("caller id", installerId);
+		assertEquals(0L, flowPathFlags);
 	}
 	
 	/**
@@ -437,9 +446,9 @@
 		final String getAllFlowsWithoutFlowEntries = "getAllFlowsWithoutFlowEntries";
 		// create mock objects
 		FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, getAllFlowsWithoutFlowEntries);
-		IFlowPath flowPath1 = createIFlowPathMock(1, "", 1, 2, 3, 4);
-		IFlowPath flowPath2 = createIFlowPathMock(5, "", 2, 3, 4, 5);
-		IFlowPath flowPath3 = createIFlowPathMock(10, "", 3, 4, 5, 6);
+		IFlowPath flowPath1 = createIFlowPathMock(1, "", 0, 1, 2, 3, 4);
+		IFlowPath flowPath2 = createIFlowPathMock(5, "", 0, 2, 3, 4, 5);
+		IFlowPath flowPath3 = createIFlowPathMock(10, "", 0, 3, 4, 5, 6);
 
 		// instantiate required objects
 		ArrayList<IFlowPath> flows = new ArrayList<IFlowPath>();
@@ -472,8 +481,8 @@
 	@Test
 	public final void testGetAllFlowsSuccessNormally() throws Exception {
 		// create mock objects
-		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 1, 1, 2, 2); 
-		IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", 2, 5, 3, 5);
+		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2); 
+		IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", 0, 2, 5, 3, 5);
 		
 		// instantiate required objects
 		ArrayList<IFlowPath> flowPaths = new ArrayList<IFlowPath>();
@@ -524,6 +533,7 @@
 		FlowPath paramFlow = new FlowPath();
 		paramFlow.setFlowId(new FlowId(100));
 		paramFlow.setInstallerId(new CallerId("installer id"));
+		paramFlow.setFlowPathFlags(new FlowPathFlags(0));
 		paramFlow.setDataPath(dataPath);
 		paramFlow.setFlowEntryMatch(match);
 		
@@ -538,6 +548,7 @@
 						FlowPath flowPath = (FlowPath)EasyMock.getCurrentArguments()[0];
 						assertEquals(flowPath.flowId().value(), 100);
 						assertEquals(flowPath.installerId().toString(), "installer id");
+						assertEquals(flowPath.flowPathFlags().flags(), 0);
 						assertEquals(flowPath.dataPath().srcPort().toString(),
 								new SwitchPort(new Dpid(1), new Port((short)3)).toString());
 
@@ -558,237 +569,13 @@
 		verifyAll();
 		assertEquals(paramFlow.flowId().value(), resultFlow.flowId().value());
 		assertEquals(paramFlow.installerId().toString(), resultFlow.installerId().toString());
+		assertEquals(paramFlow.flowPathFlags().flags(), resultFlow.flowPathFlags().flags());
 		assertEquals(paramFlow.dataPath().toString(), resultFlow.dataPath().toString());
 		assertEquals(paramFlow.flowEntryMatch().toString(), resultFlow.flowEntryMatch().toString());
 	}
 		
-	/**
-	 * Test method for {@link FlowManager#measurementStorePathFlow(FlowPath)}.
-	 * @throws Exception 
-	 */
-	@Test
-	public final void testMeasurementStorePathFlowSuccessNormally() throws Exception {
-		// instantiate required objects
-		FlowPath paramFlow = createTestFlowPath(100, "installer id", 1, 3, 2, 4);
-		Map<Long, Object> shortestPathMap = new HashMap<Long, Object>();
-		FlowManager fm = new FlowManager();
-
-		// setup expectations
-		expectInitWithContext();
-		expect((Map<Long,Object>)topoRouteService.prepareShortestPathTopo()
-				).andReturn(shortestPathMap);
-		expect(topoRouteService.getTopoShortestPath(
-				shortestPathMap,
-				paramFlow.dataPath().srcPort(),
-				paramFlow.dataPath().dstPort())).andReturn(null);
-		
-		// start the test
-		replayAll();
-		
-		fm.init(context);
-		FlowPath resultFlowPath = fm.measurementStorePathFlow(paramFlow);
-		
-		// verify the test
-		verifyAll();
-		assertEquals(paramFlow.flowId().value(), resultFlowPath.flowId().value());
-		assertEquals(paramFlow.installerId().toString(), resultFlowPath.installerId().toString());
-		assertEquals(paramFlow.dataPath().toString(), resultFlowPath.dataPath().toString());
-		assertEquals(paramFlow.flowEntryMatch().toString(), resultFlowPath.flowEntryMatch().toString());
-	}
-	
-	/**
-	 * Test method for {@link FlowManager#measurementInstallPaths(Integer)}.
-	 * @throws Exception 
-	 */
-	@Test
-	public final void testMeasurementInstallPathsSuccessNormally() throws Exception {
-		final String addFlow = "addFlow";
-
-		// create mock objects
-		FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, addFlow);
-
-		// instantiate required objects
-		FlowPath flow1 = createTestFlowPath(1, "installer id", 1, 2, 3, 4);
-		FlowPath flow2 = createTestFlowPath(2, "installer id", 2, 3, 4, 5);
-		FlowPath flow3 = createTestFlowPath(3, "installer id", 3, 4, 5, 6);
-		Map<Long, Object> shortestPathMap = new HashMap<Long, Object>();
-
-		// setup expectations
-		expectInitWithContext();
-		expect((Map<Long,Object>)topoRouteService.prepareShortestPathTopo()
-				).andReturn(shortestPathMap);
-
-		expect(topoRouteService.getTopoShortestPath(
-				shortestPathMap,
-				flow1.dataPath().srcPort(),
-				flow1.dataPath().dstPort())).andReturn(null);
-		
-		expect(topoRouteService.getTopoShortestPath(
-				shortestPathMap,
-				flow2.dataPath().srcPort(),
-				flow2.dataPath().dstPort())).andReturn(null);
-
-		expect(topoRouteService.getTopoShortestPath(
-				shortestPathMap,
-				flow3.dataPath().srcPort(),
-				flow3.dataPath().dstPort())).andReturn(null);
-
-		expectPrivate(fm, addFlow,
-				EasyMock.cmpEq(flow1),
-				EasyMock.anyObject(FlowId.class),
-				EasyMock.anyObject(String.class)).andReturn(true);
-
-		expectPrivate(fm, addFlow,
-				EasyMock.cmpEq(flow2),
-				EasyMock.anyObject(FlowId.class),
-				EasyMock.anyObject(String.class)).andReturn(true);
-
-		expectPrivate(fm, addFlow,
-				EasyMock.cmpEq(flow3),
-				EasyMock.anyObject(FlowId.class),
-				EasyMock.anyObject(String.class)).andReturn(true);
-
-		// start the test
-		replayAll();
-
-		fm.init(context);
-		fm.measurementStorePathFlow(flow1);
-		fm.measurementStorePathFlow(flow2);
-		fm.measurementStorePathFlow(flow3);
-		Boolean result = fm.measurementInstallPaths(3);
-		
-		// verify the test
-		verifyAll();
-		assertTrue(result);
-	}
-	
-	/**
-	 * Test method for {@link FlowManager#measurementGetInstallPathsTimeNsec()}.
-	 * @throws Exception 
-	 */
-	@Test
-	public final void testMeasurementGetInstallPathsTimeNsecSuccessNormally() throws Exception {
-		final String addFlow = "addFlow";
-
-		// create mock objects
-		FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, addFlow);
-		mockStaticPartial(System.class, "nanoTime");
-
-		// instantiate required objects
-		FlowPath flow1 = createTestFlowPath(1, "installer id", 1, 2, 3, 4);
-		Map<Long, Object> shortestPathMap = new HashMap<Long, Object>();
-
-		// setup expectations
-		expectInitWithContext();
-		expect(System.nanoTime()).andReturn(new Long(100000));
-		expect(System.nanoTime()).andReturn(new Long(110000));
-		expect((Map<Long,Object>)topoRouteService.prepareShortestPathTopo()
-				).andReturn(shortestPathMap);
-		expect(topoRouteService.getTopoShortestPath(
-				shortestPathMap,
-				flow1.dataPath().srcPort(),
-				flow1.dataPath().dstPort())).andReturn(null);
-		expectPrivate(fm, addFlow,
-				EasyMock.cmpEq(flow1),
-				EasyMock.anyObject(FlowId.class),
-				EasyMock.anyObject(String.class)).andReturn(true);
-		
-		// start the test
-		replayAll();
-
-		fm.init(context);
-		fm.measurementStorePathFlow(flow1).toString();
-		fm.measurementInstallPaths(1);
-		Long result = fm.measurementGetInstallPathsTimeNsec();
-		
-		// verify the test
-		verifyAll();
-		assertEquals(new Long(10000), result);
-	}
-
-	/**
-	 * Test method for {@link FlowManager#measurementGetPerFlowInstallTime()}.
-	 * @throws Exception 
-	 */
-	@Test
-	public final void testMeasurementGetPerFlowInstallTimeSuccessNormally() throws Exception {
-		final String addFlow = "addFlow";
-
-		// create mock objects
-		FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, addFlow);
-		
-		// instantiate required objects
-		FlowPath flow1 = createTestFlowPath(1, "installer id", 1, 2, 3, 4);
-		Map<Long, Object> shortestPathMap = new HashMap<Long, Object>();
-
-		// setup expectations
-		expectInitWithContext();
-		expect((Map<Long,Object>)topoRouteService.prepareShortestPathTopo()
-				).andReturn(shortestPathMap);
-
-		expect(topoRouteService.getTopoShortestPath(
-				shortestPathMap,
-				flow1.dataPath().srcPort(),
-				flow1.dataPath().dstPort())).andReturn(null);
-
-		expectPrivate(fm, addFlow,
-				EasyMock.cmpEq(flow1),
-				EasyMock.anyObject(FlowId.class),
-				EasyMock.anyObject(String.class)).andReturn(true);
-
-
-		// start the test
-		replayAll();
-
-		fm.init(context);
-		fm.measurementStorePathFlow(flow1);
-		fm.measurementInstallPaths(10);
-		String result = fm.measurementGetPerFlowInstallTime();
-				
-		// verify the test
-		verifyAll();
-		assertTrue(result.startsWith("ThreadAndTimePerFlow"));
-	}
-
-	/**
-	 * Test method for {@link FlowManager#measurementClearAllPaths()}.
-	 * @throws Exception 
-	 */
-	@Test
-	public final void testMeasurementClearAllPathsSuccessNormally() throws Exception {
-		// instantiate required objects
-		FlowPath paramFlow = createTestFlowPath(100, "installer id", 1, 3, 2, 4);
-		Map<Long, Object> shortestPathMap = new HashMap<Long, Object>();
-		FlowManager fm = new FlowManager();
-
-		// setup expectations
-		expectInitWithContext();
-		expect((Map<Long,Object>)topoRouteService.prepareShortestPathTopo()
-				).andReturn(shortestPathMap);
-		expect(topoRouteService.getTopoShortestPath(
-				shortestPathMap,
-				paramFlow.dataPath().srcPort(),
-				paramFlow.dataPath().dstPort())).andReturn(null);
-		topoRouteService.dropShortestPathTopo(shortestPathMap);
-		
-		// start the test
-		replayAll();
-		
-		fm.init(context);
-		fm.measurementStorePathFlow(paramFlow);
-		Boolean result = fm.measurementClearAllPaths();
-				
-		// verify the test
-		verifyAll();
-		assertTrue(result);
-		assertEquals(new Long(0), fm.measurementGetInstallPathsTimeNsec());
-		assertEquals("", fm.measurementGetPerFlowInstallTime());
-	}
-	
-		
 	// INetMapStorage methods
 	
-	
 	/**
 	 * Test method for {@link FlowManager#init(String)}.
 	 * @throws Exception 
@@ -980,7 +767,7 @@
 	@Test
 	public final void testClearFlowSuccessNormally() throws Exception {
 		// create mock objects
-		IFlowPath flowPath = createIFlowPathMock(123, "id", 1, 2, 3, 4);
+		IFlowPath flowPath = createIFlowPathMock(123, "id", 0, 1, 2, 3, 4);
 		IFlowEntry flowEntry1 = createMock(IFlowEntry.class);
 		IFlowEntry flowEntry2 = createMock(IFlowEntry.class);
 		IFlowEntry flowEntry3 = createMock(IFlowEntry.class);
@@ -1023,8 +810,8 @@
 	@Test
 	public final void testGetAllFlowsWithoutFlowEntriesSuccessNormally() throws Exception {
 		// create mock objects
-		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 1, 1, 2, 2); 
-		IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", 2, 5, 3, 5);
+		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2); 
+		IFlowPath iFlowPath2 = createIFlowPathMock(2, "caller id", 0, 2, 5, 3, 5);
 		
 		// instantiate required objects
 		ArrayList<IFlowPath> flowPaths = new ArrayList<IFlowPath>();
@@ -1060,7 +847,7 @@
 		final String addFlowEntry = "addFlowEntry";
 		
 		// create mock objects
-		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 1, 1, 2, 2);
+		IFlowPath iFlowPath1 = createIFlowPathMock(1, "caller id", 0, 1, 1, 2, 2);
 		IFlowEntry iFlowEntry1 = createMock(IFlowEntry.class);
 		IFlowEntry iFlowEntry2 = createMock(IFlowEntry.class);
 		FlowManager fm = createPartialMockAndInvokeDefaultConstructor(FlowManager.class, addFlowEntry);
@@ -1073,7 +860,7 @@
 		flowEntry1.setOutPort(new Port((short) 11));
 		flowEntry1.setFlowEntryId(new FlowEntryId(1));
 		flowEntry1.setFlowEntryMatch(new FlowEntryMatch());
-		flowEntry1.setFlowEntryActions(new ArrayList<FlowEntryAction>());
+		flowEntry1.setFlowEntryActions(new FlowEntryActions());
 		flowEntry1.setFlowEntryErrorState(new FlowEntryErrorState());
 		
 		FlowEntry flowEntry2 = new FlowEntry();
@@ -1083,7 +870,7 @@
 		flowEntry2.setOutPort(new Port((short) 2));
 		flowEntry2.setFlowEntryId(new FlowEntryId(2));
 		flowEntry2.setFlowEntryMatch(new FlowEntryMatch());
-		flowEntry2.setFlowEntryActions(new ArrayList<FlowEntryAction>());
+		flowEntry2.setFlowEntryActions(new FlowEntryActions());
 		flowEntry2.setFlowEntryErrorState(new FlowEntryErrorState());
 		
 		DataPath dataPath = new DataPath();
@@ -1127,13 +914,18 @@
 	public final void testInstallFlowEntryWithIFlowPathSuccessNormally() throws Exception {
 		// create mock object
 		IOFSwitch iofSwitch = createNiceMock(IOFSwitch.class);
-		IFlowPath iFlowPath = createIFlowPathMock(1, "id", 1, 2, 3, 4); 
+		IFlowPath iFlowPath = createIFlowPathMock(1, "id", 0, 1, 2, 3, 4); 
 		IFlowEntry iFlowEntry = createMock(IFlowEntry.class);
 		BasicFactory basicFactory = createMock(BasicFactory.class);
 		
 		// instantiate required objects
 		FlowManager fm = new FlowManager();
 		
+		FlowEntryAction action = new FlowEntryAction();
+		action.setActionOutput(new Port((short)2));
+		FlowEntryActions actions = new FlowEntryActions();
+		actions.addAction(action);
+
 		// setup expectations
 		expectInitWithContext();
 		expect(iFlowEntry.getFlowEntryId()).andReturn(new FlowEntryId(123).toString());
@@ -1151,7 +943,7 @@
 		expect(iFlowEntry.getMatchIpToS()).andReturn(new Byte((byte)0x3));
 		expect(iFlowEntry.getMatchSrcTcpUdpPort()).andReturn(new Short((short)40000));
 		expect(iFlowEntry.getMatchDstTcpUdpPort()).andReturn(new Short((short)80));
-		expect(iFlowEntry.getActionOutput()).andReturn(new Short((short) 2));
+		expect(iFlowEntry.getActions()).andReturn(actions.toString());
 		expect(floodlightProvider.getOFMessageFactory()).andReturn(basicFactory);
 		expect(basicFactory.getMessage(OFType.FLOW_MOD)).andReturn(new OFFlowMod());
 		expect(iofSwitch.getStringId()).andReturn(new Dpid(100).toString());
diff --git a/src/test/java/net/onrc/onos/ofcontroller/routing/TopoRouteServiceTest.java b/src/test/java/net/onrc/onos/ofcontroller/routing/TopoRouteServiceTest.java
index e6d7d16..7929eb1 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/routing/TopoRouteServiceTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/routing/TopoRouteServiceTest.java
@@ -1,21 +1,13 @@
 package net.onrc.onos.ofcontroller.routing;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 import org.easymock.EasyMock;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -25,29 +17,19 @@
 
 import com.thinkaurelius.titan.core.TitanGraph;
 import com.thinkaurelius.titan.core.TitanFactory;
-import com.tinkerpop.blueprints.Vertex;
-import com.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine;
-import com.tinkerpop.gremlin.java.GremlinPipeline;
-import com.tinkerpop.pipes.PipeFunction;
-import com.tinkerpop.pipes.branch.LoopPipe.LoopBundle;
-
-import javax.script.ScriptContext;
-import javax.script.ScriptEngine;
-import javax.script.ScriptException;
-
 import net.onrc.onos.graph.GraphDBConnection;
 import net.onrc.onos.graph.GraphDBOperation;
 import net.onrc.onos.ofcontroller.core.internal.TestDatabaseManager;
 import net.onrc.onos.ofcontroller.routing.TopoRouteService;
 import net.onrc.onos.ofcontroller.util.DataPath;
 import net.onrc.onos.ofcontroller.util.Dpid;
+import net.onrc.onos.ofcontroller.util.FlowPathFlags;
 import net.onrc.onos.ofcontroller.util.Port;
 import net.onrc.onos.ofcontroller.util.SwitchPort;
 
 /**
  * A class for testing the TopoRouteService class.
  * @see net.onrc.onos.ofcontroller.routing.TopoRouteService
- * @author Pavlin Radoslavov (pavlin@onlab.us)
  */
 @RunWith(PowerMockRunner.class)
 @PrepareForTest({TitanFactory.class, GraphDBConnection.class, GraphDBOperation.class, TopoRouteService.class})
@@ -132,6 +114,20 @@
 	String expectedResult = "1/00:00:00:00:00:00:0a:01/2;1/00:00:00:00:00:00:0a:03/2;2/00:00:00:00:00:00:0a:04/3;1/00:00:00:00:00:00:0a:06/1;";
 	assertEquals(dataPathSummaryStr, expectedResult);
 
+	// Test if we apply various Flow Path Flags
+	String expectedResult2 = "1/00:00:00:00:00:00:0a:03/2;2/00:00:00:00:00:00:0a:04/3;1/00:00:00:00:00:00:0a:06/1;";
+	String expectedResult3 = "1/00:00:00:00:00:00:0a:03/2;";
+	FlowPathFlags flowPathFlags2 = new FlowPathFlags("DISCARD_FIRST_HOP_ENTRY");
+	FlowPathFlags flowPathFlags3 = new FlowPathFlags("KEEP_ONLY_FIRST_HOP_ENTRY");
+	//
+	dataPath.applyFlowPathFlags(flowPathFlags2);
+	dataPathSummaryStr = dataPath.dataPathSummary();
+	assertEquals(dataPathSummaryStr, expectedResult2);
+	//
+	dataPath.applyFlowPathFlags(flowPathFlags3);
+	dataPathSummaryStr = dataPath.dataPathSummary();
+	assertEquals(dataPathSummaryStr, expectedResult3);
+
 	//
 	// Test Shortest-Path computation to non-existing destination
 	//
@@ -180,6 +176,20 @@
 	String expectedResult = "1/00:00:00:00:00:00:0a:01/2;1/00:00:00:00:00:00:0a:03/2;2/00:00:00:00:00:00:0a:04/3;1/00:00:00:00:00:00:0a:06/1;";
 	assertEquals(dataPathSummaryStr, expectedResult);
 
+	// Test if we apply various Flow Path Flags
+	String expectedResult2 = "1/00:00:00:00:00:00:0a:03/2;2/00:00:00:00:00:00:0a:04/3;1/00:00:00:00:00:00:0a:06/1;";
+	String expectedResult3 = "1/00:00:00:00:00:00:0a:03/2;";
+	FlowPathFlags flowPathFlags2 = new FlowPathFlags("DISCARD_FIRST_HOP_ENTRY");
+	FlowPathFlags flowPathFlags3 = new FlowPathFlags("KEEP_ONLY_FIRST_HOP_ENTRY");
+	//
+	dataPath.applyFlowPathFlags(flowPathFlags2);
+	dataPathSummaryStr = dataPath.dataPathSummary();
+	assertEquals(dataPathSummaryStr, expectedResult2);
+	//
+	dataPath.applyFlowPathFlags(flowPathFlags3);
+	dataPathSummaryStr = dataPath.dataPathSummary();
+	assertEquals(dataPathSummaryStr, expectedResult3);
+
 	//
 	// Test Shortest-Path computation to non-existing destination
 	//
diff --git a/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryActionTest.java b/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryActionTest.java
new file mode 100644
index 0000000..4db734c
--- /dev/null
+++ b/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryActionTest.java
@@ -0,0 +1,427 @@
+package net.onrc.onos.ofcontroller.util;
+
+import static org.junit.Assert.assertEquals;
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.ActionEnqueue;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.ActionOutput;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.ActionSetEthernetAddr;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.ActionSetIPv4Addr;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.ActionSetIpToS;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.ActionSetTcpUdpPort;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.ActionSetVlanId;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.ActionSetVlanPriority;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.ActionStripVlan;
+
+import org.junit.Test;
+
+public class FlowEntryActionTest {
+
+	@Test
+	public void testSetActionOutputActionOutput(){
+		FlowEntryAction act = new FlowEntryAction();
+		ActionOutput actout = act.new ActionOutput(new Port((short)42));
+		act.setActionOutput(actout);
+
+		assertEquals("action output",FlowEntryAction.ActionValues.ACTION_OUTPUT , act.actionType());
+		assertEquals("actionOutput port should be the same", actout.port(), act.actionOutput().port());
+		assertEquals("actionOutput maxlen should be the same", actout.maxLen(), act.actionOutput().maxLen());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionOutputPort(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionOutput(new Port((short)42));
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionOutputToController(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionOutputToController((short)0);
+
+		FlowEntryAction act_copy = new FlowEntryAction();
+		act_copy.setActionOutput(new Port(Port.PortValues.PORT_CONTROLLER));
+		;
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetVlanIdActionSetVlanId(){
+		FlowEntryAction act = new FlowEntryAction();
+		ActionSetVlanId actVlan = act.new ActionSetVlanId((short)42);
+		act.setActionSetVlanId(actVlan);
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_SET_VLAN_VID , act.actionType());
+		assertEquals("vlanid should be the same", actVlan.vlanId(), act.actionSetVlanId().vlanId());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetVlanIdShort(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionSetVlanId((short)42);
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetVlanPriorityActionSetVlanPriority(){
+		FlowEntryAction act = new FlowEntryAction();
+		ActionSetVlanPriority actVlan = act.new ActionSetVlanPriority((byte)42);
+		act.setActionSetVlanPriority(actVlan);
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_SET_VLAN_PCP , act.actionType());
+		assertEquals("vlan priority should be the same", actVlan.vlanPriority(), act.actionSetVlanPriority().vlanPriority());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetVlanPriorityByte(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionSetVlanPriority((byte)42);
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionStripVlanActionStripVlan(){
+		FlowEntryAction act = new FlowEntryAction();
+		ActionStripVlan actVlan = act.new ActionStripVlan();
+		act.setActionStripVlan(actVlan);
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_STRIP_VLAN , act.actionType());
+		assertEquals("vlanid should be the same", actVlan.stripVlan(), act.actionStripVlan().stripVlan());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionStripVlanBoolean(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionStripVlan(true);
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetEthernetSrcAddrActionSetEthernetAddr(){
+		FlowEntryAction act = new FlowEntryAction();
+		byte[] mac = { 1, 2, 3, 4, 5, 6 };
+		ActionSetEthernetAddr setEth = act.new ActionSetEthernetAddr(new MACAddress(mac));
+		act.setActionSetEthernetSrcAddr( setEth );
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_SET_DL_SRC , act.actionType());
+		assertEquals("addr should be the same", setEth.addr(), act.actionSetEthernetSrcAddr().addr());
+
+		
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetEthernetSrcAddrMACAddress(){
+		FlowEntryAction act = new FlowEntryAction();
+		byte[] mac = { 1, 2, 3, 4, 5, 6 };
+		act.setActionSetEthernetSrcAddr(new MACAddress(mac));
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetEthernetDstAddrActionSetEthernetAddr(){
+		FlowEntryAction act = new FlowEntryAction();
+		byte[] mac = { 1, 2, 3, 4, 5, 6 };
+		ActionSetEthernetAddr setEth = act.new ActionSetEthernetAddr(new MACAddress(mac));
+		act.setActionSetEthernetDstAddr( setEth );
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_SET_DL_DST , act.actionType());
+		assertEquals("addr should be the same", setEth.addr(), act.actionSetEthernetDstAddr().addr());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetEthernetDstAddrMACAddress(){
+		FlowEntryAction act = new FlowEntryAction();
+		byte[] mac = { 1, 2, 3, 4, 5, 6 };
+		act.setActionSetEthernetDstAddr(new MACAddress(mac));
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetIPv4SrcAddrActionSetIPv4Addr(){
+		FlowEntryAction act = new FlowEntryAction();
+		ActionSetIPv4Addr setIp = act.new ActionSetIPv4Addr(new IPv4("127.0.0.1"));
+		act.setActionSetIPv4SrcAddr( setIp );
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_SET_NW_SRC , act.actionType());
+		assertEquals("addr should be the same", setIp.addr(), act.actionSetIPv4SrcAddr().addr());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetIPv4SrcAddrIPv4(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionSetIPv4SrcAddr(new IPv4("127.0.0.1"));
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetIPv4DstAddrActionSetIPv4Addr(){
+		FlowEntryAction act = new FlowEntryAction();
+		ActionSetIPv4Addr setIp = act.new ActionSetIPv4Addr(new IPv4("127.0.0.1"));
+		act.setActionSetIPv4DstAddr( setIp );
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_SET_NW_DST , act.actionType());
+		assertEquals("addr should be the same", setIp.addr(), act.actionSetIPv4DstAddr().addr());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetIPv4DstAddrIPv4(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionSetIPv4DstAddr(new IPv4("127.0.0.1"));
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetIpToSActionSetIpToS(){
+		FlowEntryAction act = new FlowEntryAction();
+		ActionSetIpToS setIpTos = act.new ActionSetIpToS((byte)42);
+		act.setActionSetIpToS( setIpTos );
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_SET_NW_TOS , act.actionType());
+		assertEquals("tos should be the same", setIpTos.ipToS(), act.actionSetIpToS().ipToS());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetIpToSByte(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionSetIpToS((byte)1);
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetTcpUdpSrcPortActionSetTcpUdpPort(){
+		FlowEntryAction act = new FlowEntryAction();
+		ActionSetTcpUdpPort setPorts = act.new ActionSetTcpUdpPort((short)42);
+		act.setActionSetTcpUdpSrcPort( setPorts );
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_SET_TP_SRC , act.actionType());
+		assertEquals("port should be the same", setPorts.port(), act.actionSetTcpUdpSrcPort().port());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetTcpUdpSrcPortShort(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionSetTcpUdpSrcPort((short)1);
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetTcpUdpDstPortActionSetTcpUdpPort(){
+		FlowEntryAction act = new FlowEntryAction();
+		ActionSetTcpUdpPort setPorts = act.new ActionSetTcpUdpPort((short)42);
+		act.setActionSetTcpUdpDstPort( setPorts );
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_SET_TP_DST , act.actionType());
+		assertEquals("port should be the same", setPorts.port(), act.actionSetTcpUdpDstPort().port());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionSetTcpUdpDstPortShort(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionSetTcpUdpDstPort((short)1);
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionEnqueueActionEnqueue(){
+		FlowEntryAction act = new FlowEntryAction();
+		ActionEnqueue enq = act.new ActionEnqueue(new Port((short)42), 1);
+		act.setActionEnqueue( enq );
+
+		assertEquals("action type",FlowEntryAction.ActionValues.ACTION_ENQUEUE , act.actionType());
+		assertEquals("port should be the same", enq.port(), act.actionEnqueue().port());
+		assertEquals("queue id should be the same", enq.queueId(), act.actionEnqueue().queueId());
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+	@Test
+	public void testSetActionEnqueuePortInt(){
+		FlowEntryAction act = new FlowEntryAction();
+		act.setActionEnqueue(new Port((short)42), 1);
+
+		FlowEntryAction act_copy = new FlowEntryAction(act);
+		FlowEntryAction act_copy2 = new FlowEntryAction(act.toString());
+
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy.toString());
+		assertEquals("toString must match between copies", act.toString(),
+				act_copy2.toString());
+	}
+
+}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryMatchTest.java b/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryMatchTest.java
new file mode 100644
index 0000000..4381208
--- /dev/null
+++ b/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryMatchTest.java
@@ -0,0 +1,312 @@
+package net.onrc.onos.ofcontroller.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import net.floodlightcontroller.util.MACAddress;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class FlowEntryMatchTest {
+
+	FlowEntryMatch match;
+	
+	Port inport = new Port((short)1);
+	byte[] byte1 = { 1, 2, 3, 4, 5, 6 };
+	byte[] byte2 = { 6, 5, 4, 3, 2, 1 };
+	MACAddress mac1 = new MACAddress(byte1);
+	MACAddress mac2 = new MACAddress(byte2);
+	Short ether = Short.valueOf((short)2);
+	Short vlanid = Short.valueOf((short)3);
+	Byte vlanprio = Byte.valueOf((byte)4);
+	IPv4Net ip1 = new IPv4Net("127.0.0.1/32");
+	IPv4Net ip2 = new IPv4Net("127.0.0.2/32");
+	Byte ipproto = Byte.valueOf((byte)5);
+	Byte ipToS = Byte.valueOf((byte)6);
+	Short tport1 = Short.valueOf((short)7);
+	Short tport2 = Short.valueOf((short)8);
+	
+	@Before
+	public void setUp() throws Exception{
+		match = new FlowEntryMatch();
+		match.enableInPort( inport);
+		match.enableSrcMac( mac1 );
+		match.enableDstMac( mac2 );
+		match.enableEthernetFrameType( ether );
+		match.enableVlanId( vlanid );
+		match.enableVlanPriority( vlanprio );
+		match.enableSrcIPv4Net( ip1 );
+		match.enableDstIPv4Net( ip2 );
+		match.enableIpProto( ipproto );
+		match.enableIpToS( ipToS );
+		match.enableSrcTcpUdpPort( tport1 );
+		match.enableDstTcpUdpPort( tport2 );
+	}
+
+	@Test
+	public void testFlowEntryMatch(){
+		FlowEntryMatch def = new FlowEntryMatch();
+		
+		assertEquals("default null", null, def.inPort() );
+		assertEquals("default null", null, def.srcMac() );
+		assertEquals("default null", null, def.dstMac() );
+		assertEquals("default null", null, def.ethernetFrameType() );
+		assertEquals("default null", null, def.vlanId() );
+		assertEquals("default null", null, def.vlanPriority() );
+		assertEquals("default null", null, def.srcIPv4Net() );
+		assertEquals("default null", null, def.dstIPv4Net() );
+		assertEquals("default null", null, def.ipProto() );
+		assertEquals("default null", null, def.ipToS() );
+		assertEquals("default null", null, def.srcTcpUdpPort() );
+		assertEquals("default null", null, def.dstTcpUdpPort() );
+	}
+
+	@Test
+	public void testFlowEntryMatchFlowEntryMatch(){
+		FlowEntryMatch def_base = new FlowEntryMatch();
+		FlowEntryMatch def = new FlowEntryMatch(def_base);
+
+		assertEquals("default null", null, def.inPort() );
+		assertEquals("default null", null, def.srcMac() );
+		assertEquals("default null", null, def.dstMac() );
+		assertEquals("default null", null, def.ethernetFrameType() );
+		assertEquals("default null", null, def.vlanId() );
+		assertEquals("default null", null, def.vlanPriority() );
+		assertEquals("default null", null, def.srcIPv4Net() );
+		assertEquals("default null", null, def.dstIPv4Net() );
+		assertEquals("default null", null, def.ipProto() );
+		assertEquals("default null", null, def.ipToS() );
+		assertEquals("default null", null, def.srcTcpUdpPort() );
+		assertEquals("default null", null, def.dstTcpUdpPort() );
+		
+		FlowEntryMatch copy = new FlowEntryMatch( match );
+		
+		assertEquals("inport", inport, copy.inPort() );
+		assertEquals("mac1", mac1, copy.srcMac() );
+		assertEquals("mac2", mac2, copy.dstMac() );
+		assertEquals("ether", ether, copy.ethernetFrameType() );
+		assertEquals("vlan id", vlanid, copy.vlanId() );
+		assertEquals("vlan prio", vlanprio, copy.vlanPriority() );
+		assertEquals("ip1", ip1, copy.srcIPv4Net() );
+		assertEquals("ip2", ip2, copy.dstIPv4Net() );
+		assertEquals("ip proto", ipproto, copy.ipProto() );
+		assertEquals("tos", ipToS, copy.ipToS() );
+		assertEquals("src port", tport1, copy.srcTcpUdpPort() );
+		assertEquals("dst port", tport2, copy.dstTcpUdpPort() );
+
+	}
+
+	@Test
+	public void testInPort(){
+		assertEquals("inport", inport, match.inPort() );
+	}
+
+	@Test
+	public void testDisableInPort(){
+		match.disableInPort();
+		assertEquals("inport", null, match.inPort() );
+		assertFalse( match.matchInPort() );
+	}
+
+	@Test
+	public void testMatchInPort(){
+		assertTrue( match.matchInPort() );
+	}
+
+	@Test
+	public void testSrcMac(){
+		assertEquals("mac1", mac1, match.srcMac() );
+	}
+
+	@Test
+	public void testDisableSrcMac(){
+		match.disableSrcMac();
+		assertEquals("srcMac", null, match.srcMac() );
+		assertFalse( match.matchSrcMac() );
+	}
+
+	@Test
+	public void testMatchSrcMac(){
+		assertTrue( match.matchSrcMac() );
+	}
+
+	@Test
+	public void testDstMac(){
+		assertEquals("mac2", mac2, match.dstMac() );
+	}
+
+	@Test
+	public void testDisableDstMac(){
+		match.disableDstMac();
+		assertEquals("dstMac", null, match.dstMac() );
+		assertFalse( match.matchDstMac() );
+	}
+
+	@Test
+	public void testMatchDstMac(){
+		assertTrue( match.matchDstMac() );
+	}
+
+	@Test
+	public void testEthernetFrameType(){
+		assertEquals("ether", ether, match.ethernetFrameType() );
+	}
+
+	@Test
+	public void testDisableEthernetFrameType(){
+		match.disableEthernetFrameType();
+		assertEquals("ethernetFrameType", null, match.ethernetFrameType() );
+		assertFalse( match.matchEthernetFrameType() );
+	}
+
+	@Test
+	public void testMatchEthernetFrameType(){
+		assertTrue( match.matchEthernetFrameType() );
+	}
+
+	@Test
+	public void testVlanId(){
+		assertEquals("vlan id", vlanid, match.vlanId() );
+	}
+
+	@Test
+	public void testDisableVlanId(){
+		match.disableVlanId();
+		assertEquals("vlanId", null, match.vlanId() );
+		assertFalse( match.matchVlanId() );
+	}
+
+	@Test
+	public void testMatchVlanId(){
+		assertTrue( match.matchVlanId() );
+	}
+
+	@Test
+	public void testVlanPriority(){
+		assertEquals("vlan prio", vlanprio, match.vlanPriority() );
+	}
+
+	@Test
+	public void testDisableVlanPriority(){
+		match.disableVlanPriority();
+		assertEquals("vlanPriority", null, match.vlanPriority() );
+		assertFalse( match.matchVlanPriority() );
+	}
+
+	@Test
+	public void testMatchVlanPriority(){
+		assertTrue( match.matchVlanPriority() );
+	}
+
+	@Test
+	public void testSrcIPv4Net(){
+		assertEquals("ip1", ip1, match.srcIPv4Net() );
+	}
+
+	@Test
+	public void testDisableSrcIPv4Net(){
+		match.disableSrcIPv4Net();
+		assertEquals("srcIPv4Net", null, match.srcIPv4Net() );
+		assertFalse( match.matchSrcIPv4Net() );
+	}
+
+	@Test
+	public void testMatchSrcIPv4Net(){
+		assertTrue( match.matchSrcIPv4Net() );
+	}
+
+	@Test
+	public void testDstIPv4Net(){
+		assertEquals("ip2", ip2, match.dstIPv4Net() );
+	}
+
+	@Test
+	public void testDisableDstIPv4Net(){
+		match.disableDstIPv4Net();
+		assertEquals("dstIPv4Net", null, match.dstIPv4Net() );
+		assertFalse( match.matchDstIPv4Net() );
+	}
+
+	@Test
+	public void testMatchDstIPv4Net(){
+		assertTrue( match.matchDstIPv4Net() );
+	}
+
+	@Test
+	public void testIpProto(){
+		assertEquals("ip proto", ipproto, match.ipProto() );
+	}
+
+	@Test
+	public void testDisableIpProto(){
+		match.disableIpProto();
+		assertEquals("ipProto", null, match.ipProto() );
+		assertFalse( match.matchIpProto() );
+	}
+
+	@Test
+	public void testMatchIpProto(){
+		assertTrue( match.matchIpProto() );
+	}
+
+	@Test
+	public void testIpToS(){
+		assertEquals("tos", ipToS, match.ipToS() );
+	}
+
+	@Test
+	public void testDisableIpToS(){
+		match.disableIpToS();
+		assertEquals("ipToS", null, match.ipToS() );
+		assertFalse( match.matchIpToS() );
+	}
+
+	@Test
+	public void testMatchIpToS(){
+		assertTrue( match.matchIpToS() );
+	}
+
+	@Test
+	public void testSrcTcpUdpPort(){
+		assertEquals("src port", tport1, match.srcTcpUdpPort() );
+	}
+
+	@Test
+	public void testDisableSrcTcpUdpPort(){
+		match.disableSrcTcpUdpPort();
+		assertEquals("srcTcpUdpPort", null, match.srcTcpUdpPort() );
+		assertFalse( match.matchSrcTcpUdpPort() );
+	}
+
+	@Test
+	public void testMatchSrcTcpUdpPort(){
+		assertTrue( match.matchSrcTcpUdpPort() );
+	}
+
+	@Test
+	public void testDstTcpUdpPort(){
+		assertEquals("dst port", tport2, match.dstTcpUdpPort() );
+	}
+
+	@Test
+	public void testDisableDstTcpUdpPort(){
+		match.disableDstTcpUdpPort();
+		assertEquals("dstTcpUdpPort", null, match.dstTcpUdpPort() );
+		assertFalse( match.matchDstTcpUdpPort() );
+	}
+
+	@Test
+	public void testMatchDstTcpUdpPort(){
+		assertTrue( match.matchDstTcpUdpPort() );
+	}
+
+	@Test
+	public void testToString(){
+		FlowEntryMatch def = new FlowEntryMatch();
+		assertEquals("match default", def.toString(), "[]");
+		
+		assertEquals("match set", match.toString(), "[inPort=1 srcMac=01:02:03:04:05:06 dstMac=06:05:04:03:02:01 ethernetFrameType=2 vlanId=3 vlanPriority=4 srcIPv4Net=127.0.0.1/32 dstIPv4Net=127.0.0.2/32 ipProto=5 ipToS=6 srcTcpUdpPort=7 dstTcpUdpPort=8]");
+	}
+
+}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java b/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java
new file mode 100644
index 0000000..1d193c4
--- /dev/null
+++ b/src/test/java/net/onrc/onos/ofcontroller/util/FlowEntryTest.java
@@ -0,0 +1,210 @@
+package net.onrc.onos.ofcontroller.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import net.floodlightcontroller.util.MACAddress;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class FlowEntryTest {
+
+	FlowEntry entry;
+	
+	FlowId flowId = new FlowId(0x1234);
+	FlowEntryId flowEntryId = new FlowEntryId(0x5678);
+	FlowEntryMatch match;
+	FlowEntryActions actions;
+	
+	Dpid dpid = new Dpid(0xCAFE);
+	
+	Port inport = new Port((short)1);
+	byte[] byte1 = { 1, 2, 3, 4, 5, 6 };
+	byte[] byte2 = { 6, 5, 4, 3, 2, 1 };
+	MACAddress mac1 = new MACAddress(byte1);
+	MACAddress mac2 = new MACAddress(byte2);
+	Short ether = Short.valueOf((short)2);
+	Short vlanid = Short.valueOf((short)3);
+	Byte vlanprio = Byte.valueOf((byte)4);
+	IPv4Net ip1 = new IPv4Net("127.0.0.1/32");
+	IPv4Net ip2 = new IPv4Net( new IPv4("127.0.0.2"), (short)32);
+	IPv4 ipaddr1 = new IPv4("127.0.0.3");
+	IPv4 ipaddr2 = new IPv4("127.0.0.4");
+	Byte ipproto = Byte.valueOf((byte)5);
+	Byte ipToS = Byte.valueOf((byte)6);
+	Short tport1 = Short.valueOf((short)7);
+	Short tport2 = Short.valueOf((short)8);
+	Port outport = new Port((short)9);
+	Port queueport = new Port((short)10);
+	int queueId = 11;
+	
+	FlowEntryErrorState errorState = new FlowEntryErrorState( (short)12, (short)13);
+
+	
+	@Before
+	public void setUp() throws Exception{
+		entry = new FlowEntry();
+
+		flowId = new FlowId("0x1234");
+		entry.setFlowId( flowId );
+
+		flowEntryId = new FlowEntryId("0x5678");
+		entry.setFlowEntryId(flowEntryId);
+		
+		dpid = new Dpid("CA:FE");
+		entry.setDpid( dpid );
+		
+		entry.setInPort( inport );
+		entry.setOutPort( outport );
+
+		match = new FlowEntryMatch();
+		match.enableInPort( inport);
+		match.enableSrcMac( mac1 );
+		match.enableDstMac( mac2 );
+		match.enableEthernetFrameType( ether );
+		match.enableVlanId( vlanid );
+		match.enableVlanPriority( vlanprio );
+		match.enableSrcIPv4Net( ip1 );
+		match.enableDstIPv4Net( ip2 );
+		match.enableIpProto( ipproto );
+		match.enableIpToS( ipToS );
+		match.enableSrcTcpUdpPort( tport1 );
+		match.enableDstTcpUdpPort( tport2 );
+		
+		entry.setFlowEntryMatch( match );
+		
+		FlowEntryAction action = null;
+		actions = entry.flowEntryActions();
+		
+		action = new FlowEntryAction();
+		action.setActionOutput(outport);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionOutputToController((short)0);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionSetVlanId(vlanid);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionSetVlanPriority(vlanprio);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionStripVlan(true);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionSetEthernetSrcAddr(mac1);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionSetEthernetDstAddr(mac2);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionSetIPv4SrcAddr(ipaddr1);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionSetIPv4DstAddr(ipaddr2);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionSetIpToS(ipToS);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionSetTcpUdpSrcPort(tport1);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionSetTcpUdpDstPort(tport2);
+		actions.addAction(action);
+
+		action = new FlowEntryAction();
+		action.setActionEnqueue(queueport, queueId);
+		actions.addAction(action);
+		
+		entry.setFlowEntryUserState( FlowEntryUserState.FE_USER_ADD );
+		entry.setFlowEntrySwitchState( FlowEntrySwitchState.FE_SWITCH_UPDATED );
+		entry.setFlowEntryErrorState( errorState );
+
+	}
+
+	@Test
+	public void testFlowEntry(){
+		FlowEntry e = new FlowEntry();
+		
+		assertTrue( e.flowEntryActions().isEmpty() );
+		assertEquals("flowEntryUserState", FlowEntryUserState.FE_USER_UNKNOWN, e.flowEntryUserState() );
+		assertEquals("flowEntrySwitchState", FlowEntrySwitchState.FE_SWITCH_UNKNOWN, e.flowEntrySwitchState() );
+	}
+
+	@Test
+	public void testGetFlowId(){
+		assertEquals("flowId", flowId, entry.getFlowId() );
+	}
+
+	@Test
+	public void testFlowEntryId(){
+		assertEquals("flowEntryId", flowEntryId, entry.flowEntryId() );
+	}
+
+	@Test
+	public void testFlowEntryMatch(){
+		assertEquals("flowEntryMatch", match, entry.flowEntryMatch() );
+	}
+
+	@Test
+	public void testFlowEntryActions(){
+		assertEquals("flowEntryActions", actions, entry.flowEntryActions() );
+	}
+
+	@Test
+	public void testSetFlowEntryActions(){
+		FlowEntryActions actions = new FlowEntryActions();
+		entry.setFlowEntryActions( actions );
+		assertEquals("flowEntryActions", actions, entry.flowEntryActions() );
+	}
+
+	@Test
+	public void testDpid(){
+		assertEquals("dpid", dpid, entry.dpid() );
+	}
+
+	@Test
+	public void testInPort(){
+		assertEquals("inPort", inport, entry.inPort() );
+	}
+
+	@Test
+	public void testOutPort(){
+		assertEquals("outPort", outport, entry.outPort() );
+	}
+
+	@Test
+	public void testFlowEntryUserState(){
+		assertEquals("flowEntryUserState", FlowEntryUserState.FE_USER_ADD, entry.flowEntryUserState() );
+	}
+
+	@Test
+	public void testFlowEntrySwitchState(){
+		assertEquals("flowEntrySwitchState", FlowEntrySwitchState.FE_SWITCH_UPDATED, entry.flowEntrySwitchState() );
+	}
+
+	@Test
+	public void testFlowEntryErrorState(){
+		assertEquals("flowEntryErrorState", errorState, entry.flowEntryErrorState() );
+	}
+
+	@Test
+	public void testToString(){
+		FlowEntry def = new FlowEntry();
+		assertEquals( def.toString(), "[ flowEntryActions=[] flowEntryUserState=FE_USER_UNKNOWN flowEntrySwitchState=FE_SWITCH_UNKNOWN]" );
+		assertEquals( entry.toString(), "[flowEntryId=0x5678 flowEntryMatch=[inPort=1 srcMac=01:02:03:04:05:06 dstMac=06:05:04:03:02:01 ethernetFrameType=2 vlanId=3 vlanPriority=4 srcIPv4Net=127.0.0.1/32 dstIPv4Net=127.0.0.2/32 ipProto=5 ipToS=6 srcTcpUdpPort=7 dstTcpUdpPort=8] flowEntryActions=[[type=ACTION_OUTPUT action=[port=9 maxLen=0]];[type=ACTION_OUTPUT action=[port=-3 maxLen=0]];[type=ACTION_SET_VLAN_VID action=[vlanId=3]];[type=ACTION_SET_VLAN_PCP action=[vlanPriority=4]];[type=ACTION_STRIP_VLAN action=[stripVlan=true]];[type=ACTION_SET_DL_SRC action=[addr=01:02:03:04:05:06]];[type=ACTION_SET_DL_DST action=[addr=06:05:04:03:02:01]];[type=ACTION_SET_NW_SRC action=[addr=127.0.0.3]];[type=ACTION_SET_NW_DST action=[addr=127.0.0.4]];[type=ACTION_SET_NW_TOS action=[ipToS=6]];[type=ACTION_SET_TP_SRC action=[port=7]];[type=ACTION_SET_TP_DST action=[port=8]];[type=ACTION_ENQUEUE action=[port=10 queueId=11]];] dpid=00:00:00:00:00:00:ca:fe inPort=1 outPort=9 flowEntryUserState=FE_USER_ADD flowEntrySwitchState=FE_SWITCH_UPDATED flowEntryErrorState=[type=12 code=13]]" );
+	}
+
+}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java b/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java
new file mode 100644
index 0000000..89a12e5
--- /dev/null
+++ b/src/test/java/net/onrc/onos/ofcontroller/util/FlowPathTest.java
@@ -0,0 +1,182 @@
+package net.onrc.onos.ofcontroller.util;
+
+import static org.junit.Assert.*;
+import net.onrc.onos.ofcontroller.core.internal.TestableGraphDBOperation.TestFlowEntry;
+import net.onrc.onos.ofcontroller.core.internal.TestableGraphDBOperation.TestFlowPath;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class FlowPathTest {
+
+	FlowPath flowPath;
+	
+	@Before
+	public void setUp() throws Exception{
+		TestFlowPath iFlowPath = new TestFlowPath();
+		iFlowPath.setFlowIdForTest("0x1234");
+		iFlowPath.setInstallerIdForTest("installerId");
+		iFlowPath.setFlowPathFlagsForTest(0L);
+		iFlowPath.setSrcSwForTest("CA:FE");
+		iFlowPath.setSrcPortForTest((short)1);
+		iFlowPath.setDstSwForTest("BA:BE");
+		iFlowPath.setDstPortForTest((short)2);
+		
+		iFlowPath.setActionsForTest("[[type=ACTION_OUTPUT action=[port=10 maxLen=11]];[type=ACTION_OUTPUT action=[port=12 maxLen=13]];]");
+		
+		TestFlowEntry iFlowEntry = new TestFlowEntry();
+		iFlowEntry.setEntryIdForTest("0x14");
+		iFlowEntry.setDpidForTest("BE:EF");
+		iFlowEntry.setActionsForTest("[[type=ACTION_OUTPUT action=[port=23 maxLen=24]];[type=ACTION_OUTPUT action=[port=25 maxLen=26]];]");
+		iFlowEntry.setUserStateForTest("FE_USER_MODIFY");
+		iFlowEntry.setSwitchStateForTest("FE_SWITCH_UPDATE_IN_PROGRESS");
+		iFlowPath.addFlowEntryForTest(iFlowEntry);
+		
+		flowPath = new FlowPath(iFlowPath);
+	}
+
+	@Test
+	public void testFlowPath(){
+		FlowPath flowPath = new FlowPath();
+		assertFalse( flowPath.flowPathFlags().isDiscardFirstHopEntry() );
+		assertFalse( flowPath.flowPathFlags().isKeepOnlyFirstHopEntry() );
+		assertTrue( flowPath.flowEntryActions().isEmpty() );
+	}
+
+	@Test
+	public void testFlowPathIFlowPath(){
+		TestFlowPath iFlowPath = new TestFlowPath();
+		iFlowPath.setFlowIdForTest("0x1234");
+		iFlowPath.setInstallerIdForTest("installerId");
+		iFlowPath.setFlowPathFlagsForTest(0L);
+		iFlowPath.setSrcSwForTest("CA:FE");
+		iFlowPath.setSrcPortForTest((short)1);
+		iFlowPath.setDstSwForTest("BA:BE");
+		iFlowPath.setDstPortForTest((short)2);
+		
+		iFlowPath.setMatchSrcMacForTest("01:02:03:04:05:06");
+		iFlowPath.setMatchDstMacForTest("06:05:04:03:02:01");
+		iFlowPath.setMatchEthernetFrameTypeForTest((short)3);
+		iFlowPath.setMatchVlanIdForTest((short)4);
+		iFlowPath.setMatchVlanPriorityForTest((byte)5);
+		iFlowPath.setMatchSrcIpaddrForTest("127.0.0.1/32");
+		iFlowPath.setMatchDstIpaddrForTest("127.0.0.2/32");
+		iFlowPath.setMatchIpProtoForTest((byte)6);
+		iFlowPath.setMatchIpToSForTest((byte)7);
+		iFlowPath.setMatchSrcTcpUdpPortForTest((short)8);
+		iFlowPath.setMatchDstTcpUdpPortForTest((short)9);
+		
+		iFlowPath.setActionsForTest("[[type=ACTION_OUTPUT action=[port=10 maxLen=11]];[type=ACTION_OUTPUT action=[port=12 maxLen=13]];]");
+		
+		TestFlowEntry iFlowEntry = new TestFlowEntry();
+		iFlowEntry.setEntryIdForTest("0x14");
+		iFlowEntry.setDpidForTest("BE:EF");
+		iFlowEntry.setMatchInPortForTest((short)15);
+		iFlowEntry.setMatchSrcMacForTest("11:22:33:44:55:66");
+		iFlowEntry.setMatchDstMacForTest("66:55:44:33:22:11");
+		iFlowEntry.setMatchEtherFrameTypeForTest((short)16);
+		iFlowEntry.setMatchVlanIdForTest((short)17);
+		iFlowEntry.setMatchVlanPriorityForTest((byte)18);
+		iFlowEntry.setMatchSrcIpaddrForTest("127.0.0.3/32");
+		iFlowEntry.setMatchDstIpaddrForTest("127.0.0.4/32");
+		iFlowEntry.setMatchIpProtoForTest((byte)19);
+		iFlowEntry.setMatchIpToSForTest((byte)20);
+		iFlowEntry.setMatchSrcTcpUdpPortForTest((short)21);
+		iFlowEntry.setMatchDstTcpUdpPortForTest((short)22);
+		iFlowEntry.setActionsForTest("[[type=ACTION_OUTPUT action=[port=23 maxLen=24]];[type=ACTION_OUTPUT action=[port=25 maxLen=26]];]");
+		iFlowEntry.setUserStateForTest("FE_USER_MODIFY");
+		iFlowEntry.setSwitchStateForTest("FE_SWITCH_UPDATE_IN_PROGRESS");
+		iFlowPath.addFlowEntryForTest(iFlowEntry);
+		
+		FlowPath flowPath = new FlowPath(iFlowPath);
+		assertEquals(flowPath.flowId().value(), 0x1234);
+		assertEquals(flowPath.installerId().value(), "installerId");
+		assertEquals(flowPath.flowPathFlags().flags(), 0);
+		assertEquals(flowPath.dataPath().srcPort().dpid().value(), 0xCAFE);
+		assertEquals(flowPath.dataPath().srcPort().port().value(), 1);
+		assertEquals(flowPath.dataPath().dstPort().dpid().value(), 0xBABE);
+		assertEquals(flowPath.dataPath().dstPort().port().value(), 2);
+		
+		assertEquals(flowPath.flowEntryMatch().srcMac().toString(), "01:02:03:04:05:06");
+		assertEquals(flowPath.flowEntryMatch().dstMac().toString(), "06:05:04:03:02:01");
+		assertEquals(flowPath.flowEntryMatch().ethernetFrameType().shortValue(), 3);
+		assertEquals(flowPath.flowEntryMatch().vlanId().shortValue(), 4);
+		assertEquals(flowPath.flowEntryMatch().vlanPriority().shortValue(), 5);
+		assertEquals(flowPath.flowEntryMatch().srcIPv4Net().address().toString(), "127.0.0.1");
+		assertEquals(flowPath.flowEntryMatch().srcIPv4Net().prefixLen() , 32);
+		assertEquals(flowPath.flowEntryMatch().dstIPv4Net().address().toString(), "127.0.0.2");
+		assertEquals(flowPath.flowEntryMatch().dstIPv4Net().prefixLen() , 32);
+		assertEquals(flowPath.flowEntryMatch().ipProto().byteValue(), 6);
+		assertEquals(flowPath.flowEntryMatch().ipToS().byteValue(), 7);
+		assertEquals(flowPath.flowEntryMatch().srcTcpUdpPort().shortValue(), 8);
+		assertEquals(flowPath.flowEntryMatch().dstTcpUdpPort().shortValue(), 9);
+		
+		assertEquals(flowPath.flowEntryActions().toString(),"[[type=ACTION_OUTPUT action=[port=10 maxLen=11]];[type=ACTION_OUTPUT action=[port=12 maxLen=13]];]");
+		
+		assertEquals(0x14, flowPath.dataPath().flowEntries().get(0).flowEntryId().value() );
+		assertEquals(0xBEEF, flowPath.dataPath().flowEntries().get(0).dpid().value() );
+		assertEquals(15, flowPath.dataPath().flowEntries().get(0).flowEntryMatch().inPort().value() );
+		assertEquals("11:22:33:44:55:66", flowPath.dataPath().flowEntries().get(0).flowEntryMatch().srcMac().toString());
+		assertEquals("66:55:44:33:22:11", flowPath.dataPath().flowEntries().get(0).flowEntryMatch().dstMac().toString());
+		assertEquals(16, flowPath.dataPath().flowEntries().get(0).flowEntryMatch().ethernetFrameType().shortValue());
+		assertEquals(17, flowPath.dataPath().flowEntries().get(0).flowEntryMatch().vlanId().shortValue());
+		assertEquals(18, flowPath.dataPath().flowEntries().get(0).flowEntryMatch().vlanPriority().byteValue());
+		assertEquals("127.0.0.3", flowPath.dataPath().flowEntries().get(0).flowEntryMatch().srcIPv4Net().address().toString());
+		assertEquals(32, flowPath.dataPath().flowEntries().get(0).flowEntryMatch().srcIPv4Net().prefixLen());
+		assertEquals("127.0.0.4", flowPath.dataPath().flowEntries().get(0).flowEntryMatch().dstIPv4Net().address().toString());
+		assertEquals(32, flowPath.dataPath().flowEntries().get(0).flowEntryMatch().dstIPv4Net().prefixLen());
+		assertEquals(19, flowPath.dataPath().flowEntries().get(0).flowEntryMatch().ipProto().byteValue());
+		assertEquals(20, flowPath.dataPath().flowEntries().get(0).flowEntryMatch().ipToS().byteValue());
+		assertEquals(21, flowPath.dataPath().flowEntries().get(0).flowEntryMatch().srcTcpUdpPort().shortValue());
+		assertEquals(22, flowPath.dataPath().flowEntries().get(0).flowEntryMatch().dstTcpUdpPort().shortValue());
+		assertEquals("[[type=ACTION_OUTPUT action=[port=23 maxLen=24]];[type=ACTION_OUTPUT action=[port=25 maxLen=26]];]", flowPath.dataPath().flowEntries().get(0).flowEntryActions().toString());
+		assertEquals("FE_USER_MODIFY", flowPath.dataPath().flowEntries().get(0).flowEntryUserState().toString());
+		assertEquals("FE_SWITCH_UPDATE_IN_PROGRESS", flowPath.dataPath().flowEntries().get(0).flowEntrySwitchState().toString());
+	}
+
+
+	@Test
+	public void testFlowPathFlags(){
+		FlowPath flowPath = new FlowPath();
+		FlowPathFlags flags = new FlowPathFlags();
+		flags.setFlags(0);
+		flowPath.setFlowPathFlags( flags );
+		assertFalse( flowPath.flowPathFlags().isDiscardFirstHopEntry() );
+		assertFalse( flowPath.flowPathFlags().isKeepOnlyFirstHopEntry() );
+	}
+
+	@Test
+	public void testSetFlowPathFlags(){
+		FlowPath flowPath = new FlowPath();
+		FlowPathFlags flags = new FlowPathFlags("DISCARD_FIRST_HOP_ENTRY");
+		flags.setFlagsStr("KEEP_ONLY_FIRST_HOP_ENTRY");
+		flowPath.setFlowPathFlags( flags );
+		assertFalse( flowPath.flowPathFlags().isDiscardFirstHopEntry() );
+		assertTrue( flowPath.flowPathFlags().isKeepOnlyFirstHopEntry() );
+	}
+
+	@Test
+	public void testSetDataPath(){
+		FlowPath flowPath = new FlowPath();
+		DataPath dataPath = new DataPath();
+		flowPath.setDataPath( dataPath );
+		assertEquals(flowPath.dataPath(), dataPath );
+	}
+
+	@Test
+	public void testToString(){
+
+		assertEquals("[flowId=0x1234 installerId=installerId flowPathFlags=[flags=] dataPath=[src=00:00:00:00:00:00:ca:fe/1 flowEntry=[flowEntryId=0x14 flowEntryMatch=[] flowEntryActions=[[type=ACTION_OUTPUT action=[port=23 maxLen=24]];[type=ACTION_OUTPUT action=[port=25 maxLen=26]];] dpid=00:00:00:00:00:00:be:ef flowEntryUserState=FE_USER_MODIFY flowEntrySwitchState=FE_SWITCH_UPDATE_IN_PROGRESS] dst=00:00:00:00:00:00:ba:be/2] flowEntryMatch=[] flowEntryActions=[[type=ACTION_OUTPUT action=[port=10 maxLen=11]];[type=ACTION_OUTPUT action=[port=12 maxLen=13]];]]", flowPath.toString());
+	}
+
+	@Test
+	public void testCompareTo(){
+		FlowPath flowPath1 = new FlowPath();
+		flowPath1.setFlowId( new FlowId(1));
+		FlowPath flowPath2 = new FlowPath();
+		flowPath2.setFlowId( new FlowId(2));
+		
+		assertTrue( flowPath1.compareTo(flowPath2) < 0);
+	}
+
+}
diff --git a/start-onos-embedded.sh b/start-onos-embedded.sh
index b98510c..f607c8e 100755
--- a/start-onos-embedded.sh
+++ b/start-onos-embedded.sh
@@ -17,6 +17,7 @@
 #JVM_OPTS="$JVM_OPTS -XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods"
 JVM_OPTS="$JVM_OPTS -XX:+UseConcMarkSweepGC -XX:+UseAdaptiveSizePolicy -XX:+AggressiveOpts -XX:+UseFastAccessorMethods"
 JVM_OPTS="$JVM_OPTS -XX:MaxInlineSize=8192 -XX:FreqInlineSize=8192"
+JVM_OPTS="$JVM_OPTS -javaagent:lib/jamm-0.2.5.jar"
 JVM_OPTS="$JVM_OPTS -XX:CompileThreshold=1500 -XX:PreBlockSpin=8 \
 		-XX:+UseThreadPriorities \
 		-XX:ThreadPriorityPolicy=42 \
diff --git a/start-onos.sh b/start-onos.sh
index 13ea0ac..14adfb0 100755
--- a/start-onos.sh
+++ b/start-onos.sh
@@ -16,6 +16,8 @@
 
 # Set JVM options
 JVM_OPTS=""
+## If you want JaCoCo Code Coverage reports... uncomment line below
+JVM_OPTS="$JVM_OPTS -javaagent:${ONOS_HOME}/lib/jacocoagent.jar=dumponexit=true,output=file,destfile=${LOGDIR}/jacoco.exec"
 JVM_OPTS="$JVM_OPTS -server -d64"
 #JVM_OPTS="$JVM_OPTS -Xmx2g -Xms2g -Xmn800m"
 JVM_OPTS="$JVM_OPTS -Xmx1g -Xms1g -Xmn800m"
@@ -69,7 +71,8 @@
   done
 
 # Create a logback file if required
-  cat <<EOF_LOGBACK >${ONOS_LOGBACK}
+  if [ ! -f ${ONOS_LOGBACK} ]; then
+    cat <<EOF_LOGBACK >${ONOS_LOGBACK}
 <configuration scan="true" debug="true">
 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
 <encoder>
@@ -93,6 +96,7 @@
 </root>
 </configuration>
 EOF_LOGBACK
+  fi
 
   # Run floodlight
   echo "Starting ONOS controller ..."
@@ -134,7 +138,7 @@
   pids="$flpid $tdpid"
   for p in ${pids}; do
     if [ x$p != "x" ]; then
-      kill -KILL $p
+      kill -TERM $p
       echo "Killed existing process (pid: $p)"
     fi
   done
diff --git a/test-network/mininet/dev_network_core_1.py b/test-network/mininet/dev_network_core_1.py
new file mode 100755
index 0000000..a0e184e
--- /dev/null
+++ b/test-network/mininet/dev_network_core_1.py
@@ -0,0 +1,157 @@
+#!/usr/bin/python
+
+"""
+Start up a Simple topology
+"""
+from mininet.net import Mininet
+from mininet.node import Controller, RemoteController
+from mininet.log import setLogLevel, info, error, warn, debug
+from mininet.cli import CLI
+from mininet.topo import Topo
+from mininet.util import quietRun
+from mininet.moduledeps import pathCheck
+from mininet.link import Link, TCLink
+
+from sys import exit
+import os.path
+from subprocess import Popen, STDOUT, PIPE
+
+import sys
+
+
+#import argparse
+
+class MyController( Controller ):
+    def __init__( self, name, ip='127.0.0.1', port=6633, **kwargs):
+        """Init.
+           name: name to give controller
+           ip: the IP address where the remote controller is
+           listening
+           port: the port where the remote controller is listening"""
+        Controller.__init__( self, name, ip=ip, port=port, **kwargs )
+
+    def start( self ):
+        "Overridden to do nothing."
+        return
+
+    def stop( self ):
+        "Overridden to do nothing."
+        return
+
+    def checkListening( self ):
+        "Warn if remote controller is not accessible"
+        listening = self.cmd( "echo A | telnet -e A %s %d" %
+                              ( self.ip, self.port ) )
+        if 'Unable' in listening:
+            warn( "Unable to contact the remote controller"
+                  " at %s:%d\n" % ( self.ip, self.port ) )
+
+class SDNTopo( Topo ):
+    "SDN Topology"
+
+    def __init__( self, *args, **kwargs ):
+        Topo.__init__( self, *args, **kwargs )
+        sw1 = self.addSwitch('sw1', dpid='0000000000000101')
+        sw2 = self.addSwitch('sw2', dpid='0000000000000102')
+        sw3 = self.addSwitch('sw3', dpid='0000000000000103')
+        sw4 = self.addSwitch('sw4', dpid='0000000000000104')
+        sw5 = self.addSwitch('sw5', dpid='0000000000000105')
+        sw6 = self.addSwitch('sw6', dpid='0000000000000106')
+
+        host1 = self.addHost( 'host1' )
+        host2 = self.addHost( 'host2' )
+        host3 = self.addHost( 'host3' )
+        host4 = self.addHost( 'host4' )
+        host5 = self.addHost( 'host5' )
+        host6 = self.addHost( 'host6' )
+
+        self.addLink( host1, sw1 )
+        self.addLink( host2, sw2 )
+        self.addLink( host3, sw3 )
+        self.addLink( host4, sw4 )
+        self.addLink( host5, sw5 )
+        self.addLink( host6, sw6 )
+
+        self.addLink( sw1, sw2 )
+        self.addLink( sw1, sw6 )
+        self.addLink( sw2, sw3 )
+        self.addLink( sw3, sw4 )
+        self.addLink( sw3, sw6 )
+        self.addLink( sw4, sw5 )
+        self.addLink( sw5, sw6 )
+        self.addLink( sw4, sw6 )
+
+def startsshd( host ):
+    "Start sshd on host"
+    info( '*** Starting sshd\n' )
+    name, intf, ip = host.name, host.defaultIntf(), host.IP()
+    banner = '/tmp/%s.banner' % name
+    host.cmd( 'echo "Welcome to %s at %s" >  %s' % ( name, ip, banner ) )
+    host.cmd( '/usr/sbin/sshd -o "Banner %s"' % banner, '-o "UseDNS no"' )
+    info( '***', host.name, 'is running sshd on', intf, 'at', ip, '\n' )
+
+def startsshds ( hosts ):
+    for h in hosts:
+        startsshd( h )
+
+def stopsshd( ):
+    "Stop *all* sshd processes with a custom banner"
+    info( '*** Shutting down stale sshd/Banner processes ',
+          quietRun( "pkill -9 -f Banner" ), '\n' )
+
+def sdnnet(opt):
+#    os.system('/home/ubuntu/openflow/controller/controller ptcp: &')
+#    os.system('/home/ubuntu/openflow/controller/controller ptcp:7000 &')
+
+    topo = SDNTopo()
+    info( '*** Creating network\n' )
+#    net = Mininet( topo=topo, controller=RemoteController )
+    net = Mininet( topo=topo, controller=MyController, link=TCLink)
+#    dc = DebugController('c3', ip='127.0.0.1', port=7000)
+#    net.addController(dc)
+#    net.addController(controller=RemoteController)
+
+    host1, host2, host3, host4, host5, host6 = net.get( 'host1', 'host2', 'host3', 'host4', 'host5', 'host6')
+
+    ## Adding 2nd, 3rd and 4th interface to host1 connected to sw1 (for another BGP peering)
+    sw1 = net.get('sw1')
+    sw2 = net.get('sw2')
+    sw3 = net.get('sw3')
+    sw4 = net.get('sw4')
+    sw5 = net.get('sw5')
+    sw6 = net.get('sw6')
+
+    net.start()
+
+    sw2.attach('tap01_2')
+    sw3.attach('tap01_3')
+    sw4.attach('tap01_4')
+    sw4.attach('tap01_5')
+    sw5.attach('tap01_6')
+    sw6.attach('tap01_7')
+    sw1.attach('tap01_8')
+
+    host1.defaultIntf().setIP('192.168.100.141/16') 
+    host2.defaultIntf().setIP('192.168.100.142/16')
+    host3.defaultIntf().setIP('192.168.100.143/16')
+    host4.defaultIntf().setIP('192.168.100.144/16')
+    host5.defaultIntf().setIP('192.168.100.145/16')
+    host6.defaultIntf().setIP('192.168.100.146/16')
+
+    hosts = [ host1, host2, host3, host4, host5, host6 ]
+    stopsshd ()
+    startsshds ( hosts )
+
+    if opt=="cli":
+        CLI(net)
+        stopsshd()
+        net.stop()
+
+if __name__ == '__main__':
+    setLogLevel( 'info' )
+    if len(sys.argv) == 1:
+      sdnnet("cli")
+    elif len(sys.argv) == 2 and sys.argv[1] == "-n":
+      sdnnet("nocli")
+    else:
+      print "%s [-n]" % sys.argv[0]
diff --git a/test-network/mininet/dev_network_edge_2.py b/test-network/mininet/dev_network_edge_2.py
index a9026c4..50dc00c 100755
--- a/test-network/mininet/dev_network_edge_2.py
+++ b/test-network/mininet/dev_network_edge_2.py
@@ -124,7 +124,7 @@
     sw=net.get('sw%02x.%02x' % (NWID,1))
     print "center sw", sw
     sw.attach('tap%02x_1' % NWID)
-    sw.attach('tap%02x_5' % NWID)
+    sw.attach('tap%02x_2' % NWID)
 
     for i in range (NR_NODES):
         host[i].defaultIntf().setIP('192.168.%d.%d/16' % (NWID,(int(i)+1))) 
diff --git a/titan/schema/test-network.xml b/titan/schema/test-network.xml
index 630c5da..2c3b993 100644
--- a/titan/schema/test-network.xml
+++ b/titan/schema/test-network.xml
@@ -55,106 +55,126 @@
         <node id="100">
             <data key="type">port</data>
             <data key="number">1</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 1 at SEA Switch</data>
         </node>
         <node id="101">
             <data key="type">port</data>
             <data key="number">2</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 2 at SEA Switch</data>
         </node>
         <node id="102">
             <data key="type">port</data>
             <data key="number">3</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 3 at SEA Switch</data>
         </node>
         <node id="103">
             <data key="type">port</data>
             <data key="number">4</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 4 at SEA Switch</data>
         </node>
 
         <node id="104">
             <data key="type">port</data>
             <data key="number">1</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 1 at LAX Switch</data>
         </node>
         <node id="105">
             <data key="type">port</data>
             <data key="number">2</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 2 at LAX Switch</data>
         </node>
         <node id="106">
             <data key="type">port</data>
             <data key="number">3</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 3 at LAX Switch</data>
         </node>
 
         <node id="107">
             <data key="type">port</data>
             <data key="number">1</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 1 at CHI Switch</data>
         </node>
         <node id="108">
             <data key="type">port</data>
             <data key="number">2</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 2 at CHI Switch</data>
         </node>
         <node id="109">
             <data key="type">port</data>
             <data key="number">3</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 3 at CHI Switch</data>
         </node>
         <node id="110">
             <data key="type">port</data>
             <data key="number">4</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 4 at CHI Switch</data>
         </node>
 
         <node id="111">
             <data key="type">port</data>
             <data key="number">1</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 1 at IAH Switch</data>
         </node>
         <node id="112">
             <data key="type">port</data>
             <data key="number">2</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 2 at IAH Switch</data>
         </node>
         <node id="113">
             <data key="type">port</data>
             <data key="number">3</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 3 at IAH Switch</data>
         </node>
 
         <node id="114">
             <data key="type">port</data>
             <data key="number">1</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 1 at NYC Switch</data>
         </node>
         <node id="115">
             <data key="type">port</data>
             <data key="number">2</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 2 at NYC Switch</data>
         </node>
         <node id="116">
             <data key="type">port</data>
             <data key="number">3</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 3 at NYC Switch</data>
         </node>
 
         <node id="117">
             <data key="type">port</data>
             <data key="number">1</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 1 at ATL Switch</data>
         </node>
         <node id="118">
             <data key="type">port</data>
             <data key="number">2</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 2 at ATL Switch</data>
         </node>
         <node id="119">
             <data key="type">port</data>
             <data key="number">3</data>
+            <data key="state">ACTIVE</data>
             <data key="desc">port 3 at ATL Switch</data>
         </node>
 
diff --git a/web/add_flow.py b/web/add_flow.py
index ec84744..8100f22 100755
--- a/web/add_flow.py
+++ b/web/add_flow.py
@@ -129,8 +129,9 @@
   my_dst_port = my_args[5]
 
   #
-  # Extract the "match" and "action" arguments
+  # Extract the "flowPathFlags", "match" and "action" arguments
   #
+  flowPathFlags = 0L
   match = {}
   matchInPortEnabled = True		# NOTE: Enabled by default
   actions = []
@@ -149,7 +150,12 @@
     arg2 = my_args[idx]
     idx = idx + 1
 
-    if arg1 == "matchInPort":
+    if arg1 == "flowPathFlags":
+      if "DISCARD_FIRST_HOP_ENTRY" in arg2:
+	flowPathFlags = flowPathFlags + 0x1
+      if "KEEP_ONLY_FIRST_HOP_ENTRY" in arg2:
+	flowPathFlags = flowPathFlags + 0x2
+    elif arg1 == "matchInPort":
       # Just mark whether inPort matching is enabled
       matchInPortEnabled = arg2 in ['True', 'true']
       # inPort = {}
@@ -198,18 +204,19 @@
       match['dstTcpUdpPort'] = int(arg2, 0)
       # match['matchDstTcpUdpPort'] = True
     elif arg1 == "actionOutput":
-      # Just mark whether ACTION_OUTPUT action is enabled
+      # 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)
+      # If ACTION_OUTPUT is explicitly enabled, add an entry with a fake
+      # port number. We need this entry to preserve the action ordering.
+      if actionOutputEnabled == True:
+        actionOutput = {}
+        outPort = {}
+        outPort['value'] = 0xffff
+        actionOutput['port'] = outPort
+        actionOutput['maxLen'] = 0
+        action['actionOutput'] = actionOutput
+        # action['actionType'] = 'ACTION_OUTPUT'
+        actions.append(action)
       #
     elif arg1 == "actionSetVlanId":
       vlanId = {}
@@ -302,6 +309,7 @@
     'my_src_port' : my_src_port,
     'my_dst_dpid' : my_dst_dpid,
     'my_dst_port' : my_dst_port,
+    'flowPathFlags' : flowPathFlags,
     'match' : match,
     'matchInPortEnabled' : matchInPortEnabled,
     'actions' : actions,
@@ -325,6 +333,7 @@
 
   my_flow_id = parsed_args['my_flow_id']
   my_installer_id = parsed_args['my_installer_id']
+  myFlowPathFlags = parsed_args['flowPathFlags']
   match = parsed_args['match']
   matchInPortEnabled = parsed_args['matchInPortEnabled']
   actions = parsed_args['actions']
@@ -335,10 +344,15 @@
   flow_id['value'] = my_flow_id
   installer_id = {}
   installer_id['value'] = my_installer_id
+  flowPathFlags = {}
+  flowPathFlags['flags'] = myFlowPathFlags
+
+  flowEntryActions = {}
 
   flow_path = {}
   flow_path['flowId'] = flow_id
   flow_path['installerId'] = installer_id
+  flow_path['flowPathFlags'] = flowPathFlags
 
   if (len(match) > 0):
     flow_path['flowEntryMatch'] = copy.deepcopy(match)
@@ -356,6 +370,11 @@
       my_data_path['flowEntries'][idx]['flowEntryMatch'] = copy.deepcopy(match)
       idx = idx + 1
 
+
+  if (len(actions) > 0):
+    flowEntryActions['actions'] = copy.deepcopy(actions)
+    flow_path['flowEntryActions'] = flowEntryActions
+
   #
   # Set the actions for each flow entry
   # NOTE: The actions from the command line are aplied
@@ -377,11 +396,12 @@
       action['actionOutput'] = copy.deepcopy(actionOutput)
       # action['actionType'] = 'ACTION_OUTPUT'
       actions.append(copy.deepcopy(action))
+      flowEntryActions = {}
+      flowEntryActions['actions'] = copy.deepcopy(actions)
 
-      my_data_path['flowEntries'][idx]['flowEntryActions'] = copy.deepcopy(actions)
+      my_data_path['flowEntries'][idx]['flowEntryActions'] = flowEntryActions
       idx = idx + 1
 
-
   flow_path['dataPath'] = my_data_path
   debug("Flow Path: %s" % flow_path)
   return flow_path
@@ -461,7 +481,7 @@
 
 
 if __name__ == "__main__":
-  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: %s [Flags] <flow-id> <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port> [Flow Path Flags] [Match Conditions] [Actions]\n" % (sys.argv[0])
   usage_msg = usage_msg + "\n"
   usage_msg = usage_msg + "    Flags:\n"
   usage_msg = usage_msg + "        -m [monitorname]  Monitor and maintain the installed shortest path(s)\n"
@@ -472,6 +492,13 @@
   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 + "    Flow Path Flags:\n"
+  usage_msg = usage_msg + "        flowPathFlags <Flags> (flag names separated by ',')\n"
+  usage_msg = usage_msg + "\n"
+  usage_msg = usage_msg + "        Known flags:\n"
+  usage_msg = usage_msg + "            DISCARD_FIRST_HOP_ENTRY    : Discard the first-hop flow entry\n"
+  usage_msg = usage_msg + "            KEEP_ONLY_FIRST_HOP_ENTRY  : Keep only the first-hop flow entry\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"
@@ -488,18 +515,17 @@
   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 + "        actionSetVlanId <VLAN ID>\n"
+  usage_msg = usage_msg + "        actionSetVlanPriority <VLAN priority>\n"
+  usage_msg = usage_msg + "        actionStripVlan <True|False>\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 + "    Actions (not implemented yet):\n"
   usage_msg = usage_msg + "        actionEnqueue <dummy argument>\n"
 
   # app.debug = False;
diff --git a/web/get_flow.py b/web/get_flow.py
index 9e954fe..9ab55da 100755
--- a/web/get_flow.py
+++ b/web/get_flow.py
@@ -33,78 +33,182 @@
 # Sample output:
 # {"flowId":{"value":"0x5"},"installerId":{"value":"FOOBAR"},"dataPath":{"srcPort":{"dpid":{"value":"00:00:00:00:00:00:00:01"},"port":{"value":0}},"dstPort":{"dpid":{"value":"00:00:00:00:00:00:00:02"},"port":{"value":0}},"flowEntries":[{"flowEntryId":"0x1389","flowEntryMatch":null,"flowEntryActions":null,"dpid":{"value":"00:00:00:00:00:00:00:01"},"inPort":{"value":0},"outPort":{"value":1},"flowEntryUserState":"FE_USER_DELETE","flowEntrySwitchState":"FE_SWITCH_NOT_UPDATED","flowEntryErrorState":null},{"flowEntryId":"0x138a","flowEntryMatch":null,"flowEntryActions":null,"dpid":{"value":"00:00:00:00:00:00:00:02"},"inPort":{"value":9},"outPort":{"value":0},"flowEntryUserState":"FE_USER_DELETE","flowEntrySwitchState":"FE_SWITCH_NOT_UPDATED","flowEntryErrorState":null}]}}
 
+def parse_match(match):
+  result = []
+
+  inPort = match['inPort']
+  matchInPort = match['matchInPort']
+  srcMac = match['srcMac']
+  matchSrcMac = match['matchSrcMac']
+  dstMac = match['dstMac']
+  matchDstMac = match['matchDstMac']
+  ethernetFrameType = match['ethernetFrameType']
+  matchEthernetFrameType = match['matchEthernetFrameType']
+  vlanId = match['vlanId']
+  matchVlanId = match['matchVlanId']
+  vlanPriority = match['vlanPriority']
+  matchVlanPriority = match['matchVlanPriority']
+  srcIPv4Net = match['srcIPv4Net']
+  matchSrcIPv4Net = match['matchSrcIPv4Net']
+  dstIPv4Net = match['dstIPv4Net']
+  matchDstIPv4Net = match['matchDstIPv4Net']
+  ipProto = match['ipProto']
+  matchIpProto = match['matchIpProto']
+  ipToS = match['ipToS']
+  matchIpToS = match['matchIpToS']
+  srcTcpUdpPort = match['srcTcpUdpPort']
+  matchSrcTcpUdpPort = match['matchSrcTcpUdpPort']
+  dstTcpUdpPort = match['dstTcpUdpPort']
+  matchDstTcpUdpPort = match['matchDstTcpUdpPort']
+  if matchInPort == True:
+    r = "inPort: %s" % inPort['value']
+    result.append(r)
+  if matchSrcMac == True:
+    r = "srcMac: %s" % srcMac['value']
+    result.append(r)
+  if matchDstMac == True:
+    r = "dstMac: %s" % dstMac['value']
+    result.append(r)
+  if matchEthernetFrameType == True:
+    r = "ethernetFrameType: %s" % hex(ethernetFrameType)
+    result.append(r)
+  if matchVlanId == True:
+    r = "vlanId: %s" % vlanId
+    result.append(r)
+  if matchVlanPriority == True:
+    r = "vlanPriority: %s" % vlanPriority
+    result.append(r)
+  if matchSrcIPv4Net == True:
+    r = "srcIPv4Net: %s" % srcIPv4Net['value']
+    result.append(r)
+  if matchDstIPv4Net == True:
+    r = "dstIPv4Net: %s" % dstIPv4Net['value']
+    result.append(r)
+  if matchIpProto == True:
+    r = "ipProto: %s" % ipProto
+    result.append(r)
+  if matchIpToS == True:
+    r = "ipToS: %s" % ipToS
+    result.append(r)
+  if matchSrcTcpUdpPort == True:
+    r = "srcTcpUdpPort: %s" % srcTcpUdpPort
+    result.append(r)
+  if matchDstTcpUdpPort == True:
+    r = "dstTcpUdpPort: %s" % dstTcpUdpPort
+    result.append(r)
+
+  return result
+
+
+def parse_actions(actions):
+  result = []
+  for a in actions:
+    actionType = a['actionType']
+    if actionType == "ACTION_OUTPUT":
+      port = a['actionOutput']['port']['value']
+      maxLen = a['actionOutput']['maxLen']
+      r = "actionType: %s port: %s maxLen: %s" % (actionType, port, maxLen)
+      result.append(r)
+    if actionType == "ACTION_SET_VLAN_VID":
+      vlanId = a['actionSetVlanId']['vlanId']
+      r = "actionType: %s vlanId: %s" % (actionType, vlanId)
+      result.append(r)
+    if actionType == "ACTION_SET_VLAN_PCP":
+      vlanPriority = a['actionSetVlanPriority']['vlanPriority']
+      r = "actionType: %s vlanPriority: %s" % (actionType, vlanPriority)
+      result.append(r)
+    if actionType == "ACTION_STRIP_VLAN":
+      stripVlan = a['actionStripVlan']['stripVlan']
+      r = "actionType: %s stripVlan: %s" % (actionType, stripVlan)
+      result.append(r)
+    if actionType == "ACTION_SET_DL_SRC":
+      setEthernetSrcAddr = a['actionSetEthernetSrcAddr']['addr']['value']
+      r = "actionType: %s setEthernetSrcAddr: %s" % (actionType, setEthernetSrcAddr)
+      result.append(r)
+    if actionType == "ACTION_SET_DL_DST":
+      setEthernetDstAddr = a['actionSetEthernetDstAddr']['addr']['value']
+      r = "actionType: %s setEthernetDstAddr: %s" % (actionType, setEthernetDstAddr)
+      result.append(r)
+    if actionType == "ACTION_SET_NW_SRC":
+      setIPv4SrcAddr = a['actionSetIPv4SrcAddr']['addr']['value']
+      r = "actionType: %s setIPv4SrcAddr: %s" % (actionType, setIPv4SrcAddr)
+      result.append(r)
+    if actionType == "ACTION_SET_NW_DST":
+      setIPv4DstAddr = a['actionSetIPv4DstAddr']['addr']['value']
+      r = "actionType: %s setIPv4DstAddr: %s" % (actionType, setIPv4DstAddr)
+      result.append(r)
+    if actionType == "ACTION_SET_NW_TOS":
+      setIpToS = a['actionSetIpToS']['ipToS']
+      r = "actionType: %s setIpToS: %s" % (actionType, setIpToS)
+      result.append(r)
+    if actionType == "ACTION_SET_TP_SRC":
+      setTcpUdpSrcPort = a['actionSetTcpUdpSrcPort']['port']
+      r = "actionType: %s setTcpUdpSrcPort: %s" % (actionType, setTcpUdpSrcPort)
+      result.append(r)
+    if actionType == "ACTION_SET_TP_DST":
+      setTcpUdpDstPort = a['actionSetTcpUdpDstPort']['port']
+      r = "actionType: %s setTcpUdpDstPort: %s" % (actionType, setTcpUdpDstPort)
+      result.append(r)
+    if actionType == "ACTION_ENQUEUE":
+      port = a['actionEnqueue']['port']['value']
+      queueId = a['actionEnqueue']['queueId']
+      r = "actionType: %s port: %s queueId: %s" % (actionType, port, queueId)
+      result.append(r)
+
+  return result
+
+
 def print_flow_path(parsedResult):
   flowId = parsedResult['flowId']['value']
   installerId = parsedResult['installerId']['value']
+  flowPathFlags = parsedResult['flowPathFlags']['flags']
   srcSwitch = parsedResult['dataPath']['srcPort']['dpid']['value']
   srcPort = parsedResult['dataPath']['srcPort']['port']['value']
   dstSwitch = parsedResult['dataPath']['dstPort']['dpid']['value']
   dstPort = parsedResult['dataPath']['dstPort']['port']['value']
-
-  print "FlowPath: (flowId = %s installerId = %s src = %s/%s dst = %s/%s)" % (flowId, installerId, srcSwitch, srcPort, dstSwitch, dstPort)
   match = parsedResult['flowEntryMatch'];
+  actions = parsedResult['flowEntryActions']['actions']
+
+  flowPathFlagsStr = ""
+  if (flowPathFlags & 0x1):
+    if flowPathFlagsStr:
+      flowPathFlagsStr += ","
+    flowPathFlagsStr += "DISCARD_FIRST_HOP_ENTRY"
+  if (flowPathFlags & 0x2):
+    if flowPathFlagsStr:
+      flowPathFlagsStr += ","
+    flowPathFlagsStr += "KEEP_ONLY_FIRST_HOP_ENTRY"
+
+  print "FlowPath: (flowId = %s installerId = %s flowPathFlags = 0x%x(%s) src = %s/%s dst = %s/%s)" % (flowId, installerId, flowPathFlags, flowPathFlagsStr, srcSwitch, srcPort, dstSwitch, dstPort)
+
   #
-  # Print the common conditions
+  # Print the common match conditions
   #
   if match == None:
     print "   Match: %s" % (match)
   else:
-    # inPort = match['inPort']
-    # matchInPort = match['matchInPort']
-    srcMac = match['srcMac']
-    matchSrcMac = match['matchSrcMac']
-    dstMac = match['dstMac']
-    matchDstMac = match['matchDstMac']
-    ethernetFrameType = match['ethernetFrameType']
-    matchEthernetFrameType = match['matchEthernetFrameType']
-    vlanId = match['vlanId']
-    matchVlanId = match['matchVlanId']
-    vlanPriority = match['vlanPriority']
-    matchVlanPriority = match['matchVlanPriority']
-    srcIPv4Net = match['srcIPv4Net']
-    matchSrcIPv4Net = match['matchSrcIPv4Net']
-    dstIPv4Net = match['dstIPv4Net']
-    matchDstIPv4Net = match['matchDstIPv4Net']
-    ipProto = match['ipProto']
-    matchIpProto = match['matchIpProto']
-    ipToS = match['ipToS']
-    matchIpToS = match['matchIpToS']
-    srcTcpUdpPort = match['srcTcpUdpPort']
-    matchSrcTcpUdpPort = match['matchSrcTcpUdpPort']
-    dstTcpUdpPort = match['dstTcpUdpPort']
-    matchDstTcpUdpPort = match['matchDstTcpUdpPort']
-    # if matchInPort == True:
-    #  print "    inPort: %s" % inPort['value']
-    if matchSrcMac == True:
-      print "    srcMac: %s" % srcMac['value']
-    if matchDstMac == True:
-      print "    dstMac: %s" % dstMac['value']
-    if matchEthernetFrameType == True:
-      print "    ethernetFrameType: %s" % hex(ethernetFrameType)
-    if matchVlanId == True:
-      print "    vlanId: %s" % vlanId
-    if matchVlanPriority == True:
-      print "    vlanPriority: %s" % vlanPriority
-    if matchSrcIPv4Net == True:
-      print "    srcIPv4Net: %s" % srcIPv4Net['value']
-    if matchDstIPv4Net == True:
-      print "    dstIPv4Net: %s" % dstIPv4Net['value']
-    if matchIpProto == True:
-      print "    ipProto: %s" % ipProto
-    if matchIpToS == True:
-      print "    ipToS: %s" % ipToS
-    if matchSrcTcpUdpPort == True:
-      print "    srcTcpUdpPort: %s" % srcTcpUdpPort
-    if matchDstTcpUdpPort == True:
-      print "    dstTcpUdpPort: %s" % dstTcpUdpPort
+    parsedMatch = parse_match(match)
+    for l in parsedMatch:
+      print "    %s" % l
 
+  #
+  # Print the actions
+  #
+  parsedActions = parse_actions(actions)
+  for l in parsedActions:
+    print "    %s" % l
+
+  #
+  # Print each Flow Entry
+  #
   for f in parsedResult['dataPath']['flowEntries']:
     flowEntryId = f['flowEntryId']
     dpid = f['dpid']['value']
     userState = f['flowEntryUserState']
     switchState = f['flowEntrySwitchState']
     match = f['flowEntryMatch'];
-    actions = f['flowEntryActions']
+    actions = f['flowEntryActions']['actions']
+
     print "  FlowEntry: (%s, %s, %s, %s)" % (flowEntryId, dpid, userState, switchState)
 
     #
@@ -113,101 +217,16 @@
     if match == None:
       print "   Match: %s" % (match)
     else:
-      inPort = match['inPort']
-      matchInPort = match['matchInPort']
-      srcMac = match['srcMac']
-      matchSrcMac = match['matchSrcMac']
-      dstMac = match['dstMac']
-      matchDstMac = match['matchDstMac']
-      ethernetFrameType = match['ethernetFrameType']
-      matchEthernetFrameType = match['matchEthernetFrameType']
-      vlanId = match['vlanId']
-      matchVlanId = match['matchVlanId']
-      vlanPriority = match['vlanPriority']
-      matchVlanPriority = match['matchVlanPriority']
-      srcIPv4Net = match['srcIPv4Net']
-      matchSrcIPv4Net = match['matchSrcIPv4Net']
-      dstIPv4Net = match['dstIPv4Net']
-      matchDstIPv4Net = match['matchDstIPv4Net']
-      ipProto = match['ipProto']
-      matchIpProto = match['matchIpProto']
-      ipToS = match['ipToS']
-      matchIpToS = match['matchIpToS']
-      srcTcpUdpPort = match['srcTcpUdpPort']
-      matchSrcTcpUdpPort = match['matchSrcTcpUdpPort']
-      dstTcpUdpPort = match['dstTcpUdpPort']
-      matchDstTcpUdpPort = match['matchDstTcpUdpPort']
-      if matchInPort == True:
-	print "    inPort: %s" % inPort['value']
-      if matchSrcMac == True:
-	print "    srcMac: %s" % srcMac['value']
-      if matchDstMac == True:
-	print "    dstMac: %s" % dstMac['value']
-      if matchEthernetFrameType == True:
-	print "    ethernetFrameType: %s" % hex(ethernetFrameType)
-      if matchVlanId == True:
-	print "    vlanId: %s" % vlanId
-      if matchVlanPriority == True:
-	print "    vlanPriority: %s" % vlanPriority
-      if matchSrcIPv4Net == True:
-	print "    srcIPv4Net: %s" % srcIPv4Net['value']
-      if matchDstIPv4Net == True:
-	print "    dstIPv4Net: %s" % dstIPv4Net['value']
-      if matchIpProto == True:
-	print "    ipProto: %s" % ipProto
-      if matchIpToS == True:
-	print "    ipToS: %s" % ipToS
-      if matchSrcTcpUdpPort == True:
-	print "    srcTcpUdpPort: %s" % srcTcpUdpPort
-      if matchDstTcpUdpPort == True:
-	print "    dstTcpUdpPort: %s" % dstTcpUdpPort
-
+      parsedMatch = parse_match(match)
+      for l in parsedMatch:
+	print "    %s" % l
     #
     # Print the actions
     #
-    if actions == None:
-      print "   Actions: %s" % (actions)
-    else:
-      for a in actions:
-	actionType = a['actionType']
-	if actionType == "ACTION_OUTPUT":
-	  port = a['actionOutput']['port']['value']
-	  maxLen = a['actionOutput']['maxLen']
-	  print "    actionType: %s port: %s maxLen: %s" % (actionType, port, maxLen)
-	if actionType == "ACTION_SET_VLAN_VID":
-	  vlanId = a['actionSetVlanId']['vlanId']
-	  print "    actionType: %s vlanId: %s" % (actionType, vlanId)
-	if actionType == "ACTION_SET_VLAN_PCP":
-	  vlanPriority = a['actionSetVlanPriority']['vlanPriority']
-	  print "    actionType: %s vlanPriority: %s" % (actionType, vlanPriority)
-	if actionType == "ACTION_STRIP_VLAN":
-	  stripVlan = a['actionStripVlan']['stripVlan']
-	  print "    actionType: %s stripVlan: %s" % (actionType, stripVlan)
-	if actionType == "ACTION_SET_DL_SRC":
-	  setEthernetSrcAddr = a['actionSetEthernetSrcAddr']['addr']['value']
-	  print "    actionType: %s setEthernetSrcAddr: %s" % (actionType, setEthernetSrcAddr)
-	if actionType == "ACTION_SET_DL_DST":
-	  setEthernetDstAddr = a['actionSetEthernetDstAddr']['addr']['value']
-	  print "    actionType: %s setEthernetDstAddr: %s" % (actionType, setEthernetDstAddr)
-	if actionType == "ACTION_SET_NW_SRC":
-	  setIPv4SrcAddr = a['actionSetIPv4SrcAddr']['addr']['value']
-	  print "    actionType: %s setIPv4SrcAddr: %s" % (actionType, setIPv4SrcAddr)
-	if actionType == "ACTION_SET_NW_DST":
-	  setIPv4DstAddr = a['actionSetIPv4DstAddr']['addr']['value']
-	  print "    actionType: %s setIPv4DstAddr: %s" % (actionType, setIPv4DstAddr)
-	if actionType == "ACTION_SET_NW_TOS":
-	  setIpToS = a['actionSetIpToS']['ipToS']
-	  print "    actionType: %s setIpToS: %s" % (actionType, setIpToS)
-	if actionType == "ACTION_SET_TP_SRC":
-	  setTcpUdpSrcPort = a['actionSetTcpUdpSrcPort']['port']
-	  print "    actionType: %s setTcpUdpSrcPort: %s" % (actionType, setTcpUdpSrcPort)
-	if actionType == "ACTION_SET_TP_DST":
-	  setTcpUdpDstPort = a['actionSetTcpUdpDstPort']['port']
-	  print "    actionType: %s setTcpUdpDstPort: %s" % (actionType, setTcpUdpDstPort)
-	if actionType == "ACTION_ENQUEUE":
-	  port = a['actionEnqueue']['port']['value']
-	  queueId = a['actionEnqueue']['queueId']
-	  print "    actionType: %s port: %s queueId: %s" % (actionType, port, queueId)
+    parsedActions = parse_actions(actions)
+    for l in parsedActions:
+      print "    %s" % l
+
 
 def get_flow_path(flow_id):
   try:
diff --git a/web/measurement_clear_all_paths.py b/web/measurement_clear_all_paths.py
deleted file mode 100755
index 5bb73c5..0000000
--- a/web/measurement_clear_all_paths.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#! /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
deleted file mode 100755
index d64dc49..0000000
--- a/web/measurement_get_install_paths_time_nsec.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#! /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
deleted file mode 100755
index bf2bcc7..0000000
--- a/web/measurement_get_per_flow_install_time.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#! /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
deleted file mode 100755
index d99070e..0000000
--- a/web/measurement_install_paths.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#! /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
deleted file mode 100755
index 3187299..0000000
--- a/web/measurement_process.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#! /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
deleted file mode 100755
index 80d0517..0000000
--- a/web/measurement_run.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#! /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
deleted file mode 100755
index dda7fbd..0000000
--- a/web/measurement_store_flow.py
+++ /dev/null
@@ -1,446 +0,0 @@
-#! /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 == "matchEthernetFrameType":
-      match['ethernetFrameType'] = int(arg2, 0)
-      # match['matchEthernetFrameType'] = 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 == "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 == "matchIpProto":
-      match['ipProto'] = int(arg2, 0)
-      # match['matchIpProto'] = True
-    elif arg1 == "matchIpToS":
-      match['ipToS'] = int(arg2, 0)
-      # match['matchIpToS'] = 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 == "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 == "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 == "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 + "        matchEthernetFrameType <Ethernet frame type>\n"
-  usage_msg = usage_msg + "        matchVlanId <VLAN ID>\n"
-  usage_msg = usage_msg + "        matchVlanPriority <VLAN priority>\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 + "\n"
-  usage_msg = usage_msg + "        matchIpProto <IP protocol>\n"
-  usage_msg = usage_msg + "        matchIpToS <IP ToS (DSCP field, 6 bits)>\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/topology_rest.py b/web/topology_rest.py
index 53f46bc..ea33a00 100755
--- a/web/topology_rest.py
+++ b/web/topology_rest.py
@@ -216,19 +216,6 @@
   resp = Response(result, status=200, mimetype='application/json')
   return resp
 
-@app.route("/proxy/gui/switchctrl/<cmd>")
-def proxy_switch_controller_setting(cmd):
-  try:
-    command = "curl -s %s/gui/switchctrl/%s" % (ONOS_GUI3_CONTROL_HOST, cmd)
-    print command
-    result = os.popen(command).read()
-  except:
-    print "REST IF has issue"
-    exit
-
-  resp = Response(result, status=200, mimetype='application/json')
-  return resp
-
 @app.route("/proxy/gui/reset")
 def proxy_gui_reset():
   result = ""