Merge remote-tracking branch 'origin/master'
diff --git a/clean-cassandra.sh b/clean-cassandra.sh
index a8a88eb..89cb7a4 100755
--- a/clean-cassandra.sh
+++ b/clean-cassandra.sh
@@ -1,7 +1,7 @@
-#! /usr/bin/expect
+#! /usr/bin/expect -f
 set timeout 5
 spawn ~/apache-cassandra-1.1.4/bin/cassandra-cli
-expect "\[default\@unknown\]"
-send "drop onos;\r"
-expect "\[default@unknown\]"
-send "quit;\r"
+expect "unknown\]\ "
+send "drop keyspace onos;\n"
+expect "unknown\]\ "
+send "quit;\n"
diff --git a/ctrl-add-ext.sh b/ctrl-add-ext-template.sh
similarity index 100%
rename from ctrl-add-ext.sh
rename to ctrl-add-ext-template.sh
diff --git a/demo-scripts/Sprint-4/cleanup.sh b/demo-scripts/Sprint-4/cleanup.sh
new file mode 100755
index 0000000..9e05b43
--- /dev/null
+++ b/demo-scripts/Sprint-4/cleanup.sh
@@ -0,0 +1,28 @@
+#! /bin/sh
+CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+function ilink_up {
+  echo "add link at $2"
+  n=`dsh -w $1 "sudo tc qdisc show dev $2" | grep netem | wc -l`
+  if [ $n -eq 1  ]; then
+    echo "dsh -w $1 sudo tc qdisc change dev $2 root netem loss 0%"
+    dsh -w $1 "sudo tc qdisc change dev $2 root netem loss 0%"
+  else 
+    echo "dsh -w $1 sudo tc qdisc add dev $2 root netem loss 0%"
+    dsh -w $1 "sudo tc qdisc add dev $2 root netem loss 0%"
+  fi
+  echo "done"
+}
+
+ilink_up onos9vpc tapa0
+ilink_up onos10vpc tapb0
+ilink_up onos10vpc tapb1
+ilink_up onos11vpc tapc0
+
+#echo "stopping mininet"
+#dsh -g onos  'sudo mn -c'
+echo "stopping ONOS"
+dsh -g onos 'cd ONOS; ./start-onos.sh stop'
+echo "stopping Cassandra"
+dsh -g onos 'cd ONOS; ./start-cassandra.sh stop'
+echo "Removing Cassandra DB"
+dsh -g onos 'sudo rm -rf /var/lib/cassandra/*'
diff --git a/demo-scripts/Sprint-4/demo-prep.sh b/demo-scripts/Sprint-4/demo-prep.sh
new file mode 100755
index 0000000..80c3aa4
--- /dev/null
+++ b/demo-scripts/Sprint-4/demo-prep.sh
@@ -0,0 +1,69 @@
+#! /bin/bash
+export CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+function cmd {
+while [ 1 ] ; do
+   $@
+   echo "Continue? [y/n]:"
+   read -t 2 value 
+   if [ x$value == "xy" ]; then 
+     break;
+   fi
+   sleep 1
+done
+}
+
+./start-onos.sh 'onos9vpc,onos10vpc,onos11vpc,onos12vpc' stop
+./start-onos.sh 'onos9vpc,onos10vpc,onos11vpc,onos12vpc' status
+#./start-cassandra.sh 'onos9vpc' start
+#sleep 2
+#./start-cassandra.sh 'onos10vpc,onos11vpc,onos12vpc' start
+
+dsh -w onos9vpc 'cd ONOS;./clean-cassandra.sh'
+cmd ./start-cassandra.sh onos9vpc status
+sleep 5
+echo "Set mininet local"
+dsh -g onos 'ONOS/ctrl-none.sh'
+echo "Start ONOS on 9, 10 and 12"
+./start-onos.sh 'onos9vpc' start
+sleep 1
+./start-onos.sh 'onos10vpc' start
+sleep 1
+./start-onos.sh 'onos12vpc' start
+while [ 1 ] ; do
+   ./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' status
+   echo "Continue? [y/s/n]:"
+   read -t 2 value 
+   if [ x$value == "xy" ]; then 
+     break;
+   elif [ x$value == "xs" ]; then 
+     ./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' start
+   fi
+   sleep 1
+done
+sleep 2
+dsh -g onos 'ONOS/ctrl-local.sh'
+#sleep 2
+#./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' start
+while [ 1 ] ; do
+   ./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' status
+   echo "Continue? [y/s/n]:"
+   read -t 2 value 
+   if [ x$value == "xy" ]; then 
+     break;
+   elif [ x$value == "xs" ]; then 
+     ./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' start
+   fi
+   sleep 1
+done
+#cmd dsh -w onos9vpc,onos10vpc 'ONOS/ctrl-add-ext.sh'
+#while [ 1 ] ; do
+#   ./start-onos.sh 'onos9vpc,onos10vpc,onos12vpc' status
+#   echo "Continue? [y/n]:"
+#   read -t 2 value 
+#   if [ x$value == "xy" ]; then 
+#     break;
+#   fi
+#   sleep 1
+#done
+#dsh -w onos9vpc,onos10vpc 'ONOS/ctrl-add-ext.sh'
+#sleep 1
diff --git a/demo-scripts/Sprint-4/demo.sh b/demo-scripts/Sprint-4/demo.sh
new file mode 100755
index 0000000..a835daf
--- /dev/null
+++ b/demo-scripts/Sprint-4/demo.sh
@@ -0,0 +1,43 @@
+#! /bin/bash
+export CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+function cmd {
+while [ 1 ] ; do
+   $@
+   echo "Continue? [y/n]:"
+   read -t 2 value 
+   if [ x$value == "xy" ]; then 
+     break;
+   fi
+   sleep 1
+done
+}
+
+dsh -w onos9vpc,onos10vpc 'ONOS/ctrl-add-ext.sh'
+sleep 5
+
+echo "start ONOS on onos11vpc"
+read
+./start-onos.sh onos11vpc start
+echo "done"
+
+echo "kill ONOS on onos9"
+read
+./start-onos.sh onos9vpc stop
+echo "done"
+
+echo "kill ONOS on onos11"
+read
+dsh -w onos11vpc 'ONOS/ctrl-add-ext.sh'
+sleep 1
+./start-onos.sh onos11vpc stop
+echo "done"
+
+echo "bring back ONOS on onos9 and onos11"
+read
+./start-onos.sh 'onos9vpc,onos11vpc' start
+echo "done"
+
+echo "kill ONOS on onos10"
+read
+./start-onos.sh onos10vpc stop
+echo "done"
diff --git a/demo-scripts/Sprint-4/network-change.sh b/demo-scripts/Sprint-4/network-change.sh
new file mode 100755
index 0000000..4fd49f9
--- /dev/null
+++ b/demo-scripts/Sprint-4/network-change.sh
@@ -0,0 +1,138 @@
+#! /bin/sh
+
+function Wait {
+  echo "press ret> "
+  read
+}
+
+function port_down {
+  echo "Taking down $2 port $3"
+  Wait
+  dsh -w $1 "sudo ifconfig $2-$3 down"
+  echo "done"
+}
+function port_up {
+  echo "bring up $2 port $3"
+  Wait
+  dsh -w $1 "sudo ifconfig $2-$3 up"
+  echo "done"
+}
+
+function port_change {
+  port_down $1 $2 $3
+  port_up $1 $2 $3
+}
+
+function switch_remove {
+  echo "taking switch $2 out"
+  Wait
+  dsh -w $1 "sudo ovs-vsctl set-controller $2 tcp:127.0.0.1:6639"
+  echo "done"
+}
+
+function switch_back {
+  echo "taking switch $2 back"
+  Wait
+  ctrl="tcp:127.0.0.1:6633"
+  dsh -w $1 "sudo ovs-vsctl set-controller $2 $ctrl"
+  echo "done"
+}
+function switch_go_back {
+  switch_remove $1 $2
+  switch_back $1 $2
+}
+
+function link_down {
+  echo "remove link from sw $2 port $3"
+  n=`dsh -w $1 "sudo tc qdisc show dev $2-$3" | grep netem | wc -l`
+  if [ $n -eq 1  ]; then
+    echo "dsh -w $1 sudo tc qdisc change dev $2-$3 root netem loss 100%"
+    dsh -w $1 "sudo tc qdisc change dev $2-$3 root netem loss 100%"
+  else 
+    echo "dsh -w $1 sudo tc qdisc add dev $2-$3 root netem 100%"
+    dsh -w $1 "sudo tc qdisc add dev $2-$3 root netem loss 100%"
+  fi
+  echo "done"
+}
+function link_up {
+  echo "add link from sw $2 port $3"
+  n=`dsh -w $1 "sudo tc qdisc show dev $2-$3" | grep netem | wc -l`
+  if [ $n -eq 1  ]; then
+    echo "dsh -w $1 sudo tc qdisc change dev $2-$3 root netem loss 0%"
+    dsh -w $1 "sudo tc qdisc change dev $2-$3 root netem loss 0%"
+  else 
+    echo "dsh -w $1 sudo tc qdisc add dev $2-$3 root netem loss 0%"
+    dsh -w $1 "sudo tc qdisc add dev $2-$3 root netem loss 0%"
+  fi
+  echo "done"
+}
+function link_change {
+  link_down $1 $2 $3
+  link_up $1 $2 $3
+}
+
+function ilink_down {
+  echo "remove link from $2"
+  n=`dsh -w $1 "sudo tc qdisc show dev $2" | grep netem | wc -l`
+  if [ $n -eq 1  ]; then
+    echo "dsh -w $1 sudo tc qdisc change dev $2 root netem loss 100%"
+    dsh -w $1 "sudo tc qdisc change dev $2 root netem loss 100%"
+  else 
+    echo "dsh -w $1 sudo tc qdisc add dev $2 root netem 100%"
+    dsh -w $1 "sudo tc qdisc add dev $2 root netem loss 100%"
+  fi
+  echo "done"
+}
+function ilink_up {
+  echo "add link at $2"
+  n=`dsh -w $1 "sudo tc qdisc show dev $2" | grep netem | wc -l`
+  if [ $n -eq 1  ]; then
+    echo "dsh -w $1 sudo tc qdisc change dev $2 root netem loss 0%"
+    dsh -w $1 "sudo tc qdisc change dev $2 root netem loss 0%"
+  else 
+    echo "dsh -w $1 sudo tc qdisc add dev $2 root netem loss 0%"
+    dsh -w $1 "sudo tc qdisc add dev $2 root netem loss 0%"
+  fi
+  echo "done"
+}
+function ilink_change {
+  ilink_down $1 $2
+  ilink_up $1 $2
+}
+
+
+#port_change onos9vpc swa1 eth2
+#port_change onos10vpc swb3 eth4
+#port_change onos11vpc swc5 eth2
+
+#switch_go_back onos9vpc swa1
+#switch_go_back onos10vpc swb3
+#switch_go_back onos11vpc swc3
+
+#echo "link down between swa4 and swa3"
+#Wait
+#link_down onos9vpc swa4 eth2 
+#link_down onos9vpc swa3 eth4 
+#echo "link up between swa4 and swa3"
+Wait
+link_up onos9vpc swa3 eth4 
+link_up onos9vpc swa4 eth2 
+
+echo "link down between swb4 and swb3"
+Wait
+link_down onos10vpc swb4 eth2 
+link_down onos10vpc swb3 eth4 
+echo "link down between swb4 and swb3"
+Wait
+link_up onos10vpc swb3 eth4 
+link_up onos10vpc swb4 eth2 
+
+echo "link down between network 1 and network2"
+Wait
+ilink_down onos9vpc tapa0 
+ilink_down onos10vpc tapb0 
+
+echo "link up between network 1 and network2"
+Wait
+ilink_up onos10vpc tapb0 
+ilink_up onos9vpc tapa0 
diff --git a/demo-scripts/Sprint-4/start-cassandra.sh b/demo-scripts/Sprint-4/start-cassandra.sh
new file mode 100755
index 0000000..860a320
--- /dev/null
+++ b/demo-scripts/Sprint-4/start-cassandra.sh
@@ -0,0 +1,6 @@
+#! /bin/sh
+if [ $# != 2 ]; then
+  echo "$0 server cmd"
+fi
+CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+dsh -w $1 "cd ONOS; sudo ./start-cassandra.sh $2" 
diff --git a/demo-scripts/Sprint-4/start-onos.sh b/demo-scripts/Sprint-4/start-onos.sh
new file mode 100755
index 0000000..3c90e87
--- /dev/null
+++ b/demo-scripts/Sprint-4/start-onos.sh
@@ -0,0 +1,6 @@
+#! /bin/sh
+if [ $# != 2 ]; then
+  echo "$0 server cmd"
+fi
+CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+dsh -w $1 "cd ONOS; sudo ./start-onos.sh $2" 
diff --git a/demo-scripts/Sprint-4/update.sh b/demo-scripts/Sprint-4/update.sh
new file mode 100755
index 0000000..30c9391
--- /dev/null
+++ b/demo-scripts/Sprint-4/update.sh
@@ -0,0 +1,4 @@
+#! /bin/sh
+CLUSTER=/home/masayosi/bin/hosts-3x3.txt
+
+dsh -g onos 'cd ONOS; git pull; ant'
diff --git a/onos.properties b/onos.properties
deleted file mode 100644
index 498fce5..0000000
--- a/onos.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-floodlight.modules = net.floodlightcontroller.storage.memory.MemoryStorageSource,\
-net.floodlightcontroller.core.FloodlightProvider,\
-net.floodlightcontroller.threadpool.ThreadPool,\
-net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl,\
-net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
-net.floodlightcontroller.firewall.Firewall,\
-net.floodlightcontroller.forwarding.Forwarding,\
-net.floodlightcontroller.jython.JythonDebugInterface,\
-net.floodlightcontroller.counter.CounterStore,\
-net.floodlightcontroller.perfmon.PktInProcessingTime,\
-net.floodlightcontroller.ui.web.StaticWebRoutable,\
-net.floodlightcontroller.onoslistener.OnosPublisher, \
-net.onrc.onos.registry.controller.ZookeeperRegistry
-net.floodlightcontroller.restserver.RestApiServer.port = 8080
-net.floodlightcontroller.core.FloodlightProvider.openflowport = 6633
-net.floodlightcontroller.jython.JythonDebugInterface.port = 6655
-net.floodlightcontroller.forwarding.Forwarding.idletimeout = 5
-net.floodlightcontroller.forwarding.Forwarding.hardtimeout = 0
-net.floodlightcontroller.onoslistener.OnosPublisher.dbconf = /tmp/cassandra.titan
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
new file mode 100644
index 0000000..2819253
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRoute.java
@@ -0,0 +1,181 @@
+package net.floodlightcontroller.bgproute;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+
+import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.topology.ITopologyListener;
+import net.floodlightcontroller.topology.ITopologyService;
+import net.floodlightcontroller.restclient.RestClient;
+
+import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BgpRoute implements IFloodlightModule, IBgpRouteService, ITopologyListener {
+	
+	protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
+
+	protected IFloodlightProviderService floodlightProvider;
+	protected ITopologyService topology;
+	
+	protected static Ptree ptree;
+	
+	@Override
+	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+		Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(IBgpRouteService.class);
+		return l;
+	}
+
+	@Override
+	public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+		Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+		m.put(IBgpRouteService.class, this); 
+		return m;
+	}
+
+	protected IRestApiService restApi;
+	
+	@Override
+	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+		Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(IFloodlightProviderService.class);
+		l.add(ITopologyService.class);
+		l.add(IBgpRouteService.class);
+		return l;
+	}
+	
+	@Override
+	public void init(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+	    
+	    ptree = new Ptree(32);
+		
+		// Register floodlight provider and REST handler.
+		floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+		restApi = context.getServiceImpl(IRestApiService.class);
+		topology = context.getServiceImpl(ITopologyService.class);
+		
+		// Test.
+		//test();
+	}
+
+	public Ptree getPtree() {
+		return ptree;
+	}
+	
+	// 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;
+	}
+	
+	@SuppressWarnings("unused")
+    private void test() {
+		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);
+		}
+
+	}
+	
+	@Override
+	public void startUp(FloodlightModuleContext context) {
+		restApi.addRestletRoutable(new BgpRouteWebRoutable()); 
+		topology.addListener((ITopologyListener) this);
+	}
+
+	@Override
+	public void topologyChanged() {
+		boolean change = false;
+		String changelog = "";
+		
+		for (LDUpdate ldu : topology.getLastLinkUpdates()) {
+			if (ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.PORT_DOWN)) {
+				change = true;
+				changelog = changelog + " down ";
+			} else if (ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.PORT_UP)) {
+				change = true;
+				changelog = changelog + " up ";
+			}
+		}
+		log.info ("received topo change" + changelog);
+
+		if (change) {
+			RestClient.get ("http://localhost:5000/topo_change");
+		}
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
new file mode 100644
index 0000000..d5abb5a
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteResource.java
@@ -0,0 +1,139 @@
+package net.floodlightcontroller.bgproute;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.Post;
+import org.restlet.resource.Delete;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import net.floodlightcontroller.restclient.RestClient;
+
+public class BgpRouteResource extends ServerResource {
+    
+	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");
+		String output = "";
+		IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
+                get(IBgpRouteService.class.getCanonicalName());
+		
+		if (dest != null) {
+			Prefix p = new Prefix(dest, 32);
+			if (p == null) {
+				return "[GET]: dest address format is wrong";
+			}
+			byte [] nexthop = bgpRoute.lookupRib(p.getAddress()).nextHop.getAddress();
+			if (nexthop != null) {
+				output += "{\"result\": \"" + addrToString(nexthop) + "\"}\n";
+			} else {
+				output += "{\"result\": \"Nexthop does not exist\"}\n";
+			}
+		} else {
+			Ptree ptree = bgpRoute.getPtree();
+			output += "{\n  \"rib\": [\n";
+			boolean printed = false;
+			for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
+				if (node.rib == null) {
+					continue;
+				}
+				if (printed == true) {
+					output += ",\n";
+				}
+				output += "    {\"prefix\": \"" + addrToString(node.key) + "/" + node.keyBits +"\", ";
+				output += "\"nexthop\": \"" + addrToString(node.rib.nextHop.getAddress()) +"\"}";
+				printed = true;
+			}
+			//output += "{\"router_id\": \"" + addrToString(node.rib.routerId.getAddress()) +"\"}\n";
+			output += "\n  ]\n}\n";
+		}
+		
+		return output;
+	}
+	@Post
+	public String store(String fmJson) {
+        IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
+                get(IBgpRouteService.class.getCanonicalName());
+
+	    Ptree ptree = bgpRoute.getPtree();
+
+		String router_id = (String) getRequestAttributes().get("routerid");
+		String prefix = (String) getRequestAttributes().get("prefix");
+		String mask = (String) getRequestAttributes().get("mask");
+		String nexthop = (String) getRequestAttributes().get("nexthop");
+		String capability = (String) getRequestAttributes().get("capability");
+		String reply = null;
+		
+		if (capability == null) {
+			// this is a prefix add
+			Prefix p = new Prefix(prefix, Integer.valueOf(mask));
+			PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
+			Rib rib = new Rib(router_id, nexthop, p.masklen);
+
+			if (node.rib != null) {
+				node.rib = null;
+				ptree.delReference(node);
+			}
+			node.rib = rib;
+			
+			reply = "[POST: " + prefix + "/" + mask + ":" + nexthop + "]";
+			log.info(reply);
+			
+			RestClient.get("http://localhost:5000/bgp_update");
+		}
+		
+		return reply + "\n";
+	}
+	
+	@Delete
+	public String delete(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");
+		String nextHop = (String) getRequestAttributes().get("nexthop");
+		String capability = (String) getRequestAttributes().get("capability");
+		String reply = null;
+		
+		if (capability == null) {
+			// this is a prefix delete
+			Prefix p = new Prefix(prefix, Integer.valueOf(mask));
+			PtreeNode node = ptree.lookup(p.getAddress(), p.masklen);
+			Rib r = new Rib(routerId, nextHop, p.masklen);
+
+			if (node != null && node.rib != null) {
+				if (r.equals(node.rib)) {
+					node.rib = null;
+					ptree.delReference(node);
+				}
+			}
+			
+			reply = "[DELE: " + prefix + "/" + mask + ":" + nextHop + "]";
+			log.info(reply);
+			
+			RestClient.get("http://localhost:5000/bgp_update");
+		}
+
+		return reply + "\n";
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/BgpRouteWebRoutable.java b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteWebRoutable.java
new file mode 100644
index 0000000..37d5696
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/BgpRouteWebRoutable.java
@@ -0,0 +1,24 @@
+package net.floodlightcontroller.bgproute;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+public class BgpRouteWebRoutable implements RestletRoutable {
+	@Override
+	public Restlet getRestlet(Context context) {
+		Router router = new Router(context);
+		router.attach("/json", BgpRouteResource.class);
+		router.attach("/rib/{dest}", BgpRouteResource.class);
+		router.attach("/{routerid}/{prefix}/{mask}/{nexthop}", BgpRouteResource.class);
+		router.attach("/{routerid}/{capability}", BgpRouteResource.class);
+		return router;
+	}
+	
+	@Override
+	public String basePath() {
+		return "/wm/bgp";
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java b/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
new file mode 100644
index 0000000..62bdf5e
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/IBgpRouteService.java
@@ -0,0 +1,11 @@
+package net.floodlightcontroller.bgproute;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+
+public interface IBgpRouteService extends IFloodlightService {
+
+    public Rib lookupRib(byte[] dest);
+    
+    public Ptree getPtree();
+    
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/Prefix.java b/src/main/java/net/floodlightcontroller/bgproute/Prefix.java
new file mode 100644
index 0000000..7ba014b
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/Prefix.java
@@ -0,0 +1,35 @@
+package net.floodlightcontroller.bgproute;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class Prefix {
+	public int masklen;
+	protected InetAddress address;
+
+	Prefix(byte[] addr, int masklen) {
+		try {
+			address = InetAddress.getByAddress(addr);
+		} catch (UnknownHostException e) {
+			System.out.println("InetAddress exception");
+			return;
+		}
+		this.masklen = masklen;
+		System.out.println(address.toString() + "/" + masklen);
+	}
+	
+	Prefix(String str, int masklen) {
+		try {
+			address = InetAddress.getByName(str);
+			//System.out.println(address.toString());
+		} catch (UnknownHostException e) {
+			System.out.println("InetAddress exception");
+			return;
+		}
+		this.masklen = masklen;
+	}
+	
+	public byte [] getAddress() {
+		return address.getAddress();
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/Ptree.java b/src/main/java/net/floodlightcontroller/bgproute/Ptree.java
new file mode 100644
index 0000000..d53789e
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/Ptree.java
@@ -0,0 +1,316 @@
+package net.floodlightcontroller.bgproute;
+
+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 };
+	
+	// Constructor.
+	Ptree(int max_key_bits) {
+		maxKeyBits = max_key_bits;
+		maxKeyOctets = bit_to_octet(max_key_bits); 
+		refCount = 0;
+	}
+
+	public PtreeNode acquire(byte [] key) {
+		return acquire(key, maxKeyBits);
+	}
+	
+	public PtreeNode acquire(byte [] key, int key_bits) {
+		if (key_bits > maxKeyBits) {
+			return null;
+		}
+		
+		PtreeNode node = top;
+		PtreeNode match = null;
+		
+		while (node != null
+				&& node.keyBits <= key_bits
+				&& key_match(node.key, node.keyBits, key, key_bits) == true) {
+		    if (node.keyBits == key_bits) {
+				return addReference(node);
+			}
+
+			match = node;
+			
+			if (bit_check(key, node.keyBits) == true) {
+				node = node.right;
+			} else {
+				node = node.left;
+			}
+		}
+
+		PtreeNode add = null;
+		
+		if (node == null) {
+			add = new PtreeNode(key, key_bits, maxKeyOctets);
+			
+			if (match != null) {
+				node_link(match, add);
+			} else {
+				top = add;
+			}
+		} else {
+			add = node_common(node, key, key_bits);
+			if (add == null) {
+				return null;
+			}				
+			
+			if (match != null) {
+				node_link(match, add);
+			} else {
+				top = add;
+			}
+			node_link(add, node);
+			
+			if (add.keyBits != key_bits) {
+				match = add;
+				
+				add = new PtreeNode(key, key_bits, maxKeyOctets);
+				node_link(match, add);
+			}
+		}
+		
+		return addReference(add);
+	}
+
+	public PtreeNode lookup(byte [] key, int key_bits) {
+		if (key_bits > maxKeyBits) {
+			return null;
+		}
+		
+		PtreeNode node = top;
+		
+		while (node != null
+				&& node.keyBits <= key_bits
+				&& key_match(node.key, node.keyBits, key, key_bits) == true) {
+			if (node.keyBits == key_bits) {
+				return addReference(node);
+			}
+			
+			if (bit_check(key, node.keyBits) == true) {
+				node = node.right;
+			} else {
+				node = node.left;
+			}
+		}
+		return null;
+	}
+	
+	public PtreeNode match(byte [] key, int key_bits) {
+		if (key_bits > maxKeyBits) {
+			return null;
+		}
+		PtreeNode node = top;
+		PtreeNode matched = null;
+
+		if(node!=null)
+		
+		while (node != null
+				&& node.keyBits <= key_bits
+				&& key_match(node.key, node.keyBits, key, key_bits) == true) {
+			matched = node;
+			
+			if (bit_check(key, node.keyBits) == true) {
+				node = node.right;
+			} else {
+				node = node.left;
+			}
+		}
+		
+		if (matched != null) {
+			return addReference(matched);
+		}
+		
+		return null;
+	}
+	
+	public PtreeNode begin() {
+		if (top == null) {
+			return null;
+		}
+		return addReference(top);
+	}
+	
+	public PtreeNode next(PtreeNode node) {
+		PtreeNode next;
+		
+		if (node.left != null) {
+			next = node.left;
+			addReference(next);
+			delReference(node);
+			return next;
+		}
+		if (node.right != null) {
+			next = node.right;
+			addReference(next);
+			delReference(node);
+			return next;
+		}
+		
+		PtreeNode 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;
+			}
+			node = node.parent;
+		}
+		
+		delReference(start);
+		
+		return null;
+	}
+
+	static public int bit_to_octet(int key_bits) {
+		return Math.max((key_bits + 7) / 8, 1);
+	}
+
+	private PtreeNode addReference(PtreeNode node) {
+		node.refCount++;
+		return node;
+	}
+	
+	public void delReference(PtreeNode node) {
+		if (node.refCount > 0) {
+			node.refCount--;
+		}
+		if (node.refCount == 0) {
+			node_remove(node);
+		}
+	}
+	
+	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;
+		}
+		
+		offset = (Math.min(key1_len, key2_len)) / 8;
+		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(PtreeNode node, PtreeNode add) {
+		boolean bit = bit_check(add.key, node.keyBits);
+		
+		if (bit == true) {
+			node.right = add;
+		} else {
+			node.left = add;
+		}
+		add.parent = node;
+	}
+	
+	@SuppressWarnings("unused")
+    private PtreeNode node_common(PtreeNode node, byte [] key, int key_bits) {
+		int i;
+		int limit = Math.min(node.keyBits, key_bits) / 8;
+
+		for (i = 0; i < limit; i++) {
+			if (node.key[i] != key[i]) {
+				break;
+			}
+		}
+		
+		int common_len = i * 8;
+		int boundary = 0;
+
+		if (common_len != key_bits) {
+			byte diff = (byte)(node.key[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++;
+			}
+		}
+		
+		PtreeNode add = new PtreeNode(null, common_len, maxKeyOctets);
+		if (add == null)
+			return null;
+		
+		int j;
+		for (j = 0; j < i; j++)
+			add.key[j] = node.key[j];
+
+		if (boundary != 0)
+			add.key[j] = (byte)(node.key[j] & maskBits[add.keyBits % 8]);
+		
+		return add;
+	}
+	
+	private void node_remove(PtreeNode node) {
+		PtreeNode child;
+		PtreeNode parent;
+		
+		if (node.left != null && node.right != null) {
+			return;
+		}
+		
+		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;
+		}
+		
+		if (parent != null && parent.refCount == 0) {
+			node_remove(parent);
+		}
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/PtreeNode.java b/src/main/java/net/floodlightcontroller/bgproute/PtreeNode.java
new file mode 100644
index 0000000..4d2c265
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/PtreeNode.java
@@ -0,0 +1,44 @@
+package net.floodlightcontroller.bgproute;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PtreeNode {
+	public PtreeNode parent;
+	public PtreeNode left;
+	public PtreeNode right;
+	
+	public byte key[];
+	public int keyBits;
+	
+	public int refCount;
+	
+	public Rib rib;
+	protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
+	
+	PtreeNode(byte [] key, int key_bits, int max_key_octet) {
+		parent = null;
+		left = null;
+		right = null;
+		refCount = 0;
+		rib = null;
+		this.key = new byte[max_key_octet];
+		this.keyBits = key_bits;
+		log.debug("inside Ptreenode constructor key {} bits {}", key, key_bits);
+		
+		int octet = Ptree.bit_to_octet(key_bits);
+		for (int i = 0; i < max_key_octet; i++) {
+			if (i < octet) {
+				if (key != null) {
+				    log.debug(octet + ": filling key[{}] {}", i, key[i]);
+				    this.key[i] = key[i];
+				} else {
+				    log.debug("no filling, null key", i);
+				}
+			} else {
+			    log.debug("filling key {} as 0", i);
+				this.key[i] = 0;
+			}
+		}
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/bgproute/Rib.java b/src/main/java/net/floodlightcontroller/bgproute/Rib.java
new file mode 100644
index 0000000..71868ff
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/bgproute/Rib.java
@@ -0,0 +1,45 @@
+package net.floodlightcontroller.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 == r.routerId && this.nextHop == r.nextHop && this.masklen == r.masklen;
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
index 4af1deb..8788aa7 100644
--- a/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/floodlightcontroller/core/INetMapTopologyObjects.java
@@ -132,4 +132,101 @@
 		@GremlinGroovy("_().in('host').in('on').path(){it.number}{it.dpid}")
 		public Iterable<SwitchPort> getAttachmentPoints();*/
 	}
+
+public interface IFlowPath extends IBaseObject {
+		@Property("flow_id")
+		public String getFlowId();
+
+		@Property("flow_id")
+		public void setFlowId(String flowId);
+
+		@Property("installer_id")
+		public String getInstallerId();
+
+		@Property("installer_id")
+		public void setInstallerId(String installerId);
+
+		@Property("src_switch")
+		public String getSrcSwitch();
+
+		@Property("src_switch")
+		public void setSrcSwitch(String srcSwitch);
+
+		@Property("src_port")
+		public Short getSrcPort();
+
+		@Property("src_port")
+		public void setSrcPort(Short srcPort);
+
+		@Property("dst_switch")
+		public String getDstSwitch();
+
+		@Property("dst_switch")
+		public void setDstSwitch(String dstSwitch);
+
+		@Property("dst_port")
+		public Short getDstPort();
+
+		@Property("dst_port")
+		public void setDstPort(Short dstPort);
+
+		@Adjacency(label="flow", direction=Direction.IN)
+		public Iterable<IFlowEntry> getFlowEntries();
+
+		@Adjacency(label="flow", direction=Direction.IN)
+		public void addFlowEntry(final IFlowEntry flowEntry);
+
+		@Adjacency(label="flow", direction=Direction.IN)
+		public void removeFlowEntry(final IFlowEntry flowEntry);
+	}
+
+public interface IFlowEntry extends IBaseObject {
+		@Property("flow_entry_id")
+		public String getFlowEntryId();
+
+		@Property("flow_entry_id")
+		public void setFlowEntryId(String flowEntryId);
+
+		@Property("switch_dpid")
+		public String getSwitchDpid();
+
+		@Property("switch_dpid")
+		public void setSwitchDpid(String switchDpid);
+
+		@Property("in_port")
+		public Short getInPort();
+
+		@Property("in_port")
+		public void setInPort(Short inPort);
+
+		@Property("out_port")
+		public Short getOutPort();
+
+		@Property("out_port")
+		public void setOutPort(Short outPort);
+
+		@Property("user_state")
+		public String getUserState();
+
+		@Property("user_state")
+		public void setUserState(String userState);
+
+		@Property("switch_state")
+		public String getSwitchState();
+
+		@Property("switch_state")
+		public void setSwitchState(String switchState);
+
+		@Property("error_state_type")
+		public String getErrorStateType();
+
+		@Property("error_state_type")
+		public void setErrorStateType(String errorStateType);
+
+		@Property("error_state_code")
+		public String getErrorStateCode();
+
+		@Property("error_state_code")
+		public void setErrorStateCode(String errorStateCode);
+	}
 }
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index 3e657d1..d4a44fc 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -489,36 +489,25 @@
 				
 				Role role = null;
 				
-				if (sw.getRole() == null){
-					if (hasControl){
-						role = Role.MASTER;
-					}
-					else {
-						role = Role.SLAVE;
-					}
-				}
-				else if (hasControl && sw.getRole() == Role.SLAVE) {
-					// Send a MASTER role request to the switch.
-					// If this is the first role request, 
-                    // this is a probe that we'll use to determine if the switch
-                    // actually supports the role request message. If it does we'll
-                    // get back a role reply message. If it doesn't we'll get back an
-                    // OFError message. 
-                    // If role is MASTER we will promote switch to active
-                    // list when we receive the switch's role reply messages
+				/*
+				 * issue #229
+				 * Cannot rely on sw.getRole() as it can be behind due to pending
+				 * role changes in the queue. Just submit it and late the RoleChanger
+				 * handle duplicates.
+				 */
+
+				if (hasControl){
 					role = Role.MASTER;
 				}
-				else if (!hasControl && sw.getRole() == Role.MASTER) {
-					//Send a SLAVE role request to the switch
+				else {
 					role = Role.SLAVE;
 				}
-				
-				if (role != null) {
-					log.debug("Sending role request {} msg to {}", role, sw);
-	                Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
-	                swList.add(sw);
-	                roleChanger.submitRequest(swList, role);
-				}
+
+				log.debug("Sending role request {} msg to {}", role, sw);
+				Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
+				swList.add(sw);
+				roleChanger.submitRequest(swList, role);
+
 			}
 			
 		}
diff --git a/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java b/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java
index 3039679..6be26cb 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/SwitchStorageImpl.java
@@ -58,6 +58,7 @@
             }
 		} catch (TitanException e) {
              // TODO: handle exceptions
+			graph.stopTransaction(Conclusion.FAILURE);
 			log.info("SwitchStorage:setStatus dpid:{} state: {} failed", dpid, state);
 		}
             	
@@ -98,6 +99,7 @@
             }
 		} catch (TitanException e) {
              // TODO: handle exceptions
+			graph.stopTransaction(Conclusion.FAILURE);
 			log.error("SwitchStorage:addPort dpid:{} port:{} failed", dpid, port.getPortNumber());
 		}	
 
@@ -149,6 +151,7 @@
             /*
              * retry till we succeed?
              */
+    	graph.stopTransaction(Conclusion.FAILURE);
     	log.info("SwitchStorage:addSwitch dpid:{} failed", dpid);
     }
 
@@ -168,6 +171,7 @@
             }
 		} catch (TitanException e) {
              // TODO: handle exceptions
+			graph.stopTransaction(Conclusion.FAILURE);
 			log.error("SwitchStorage:deleteSwitch {} failed", dpid);
 		}
 
@@ -190,6 +194,7 @@
             }
 		} catch (TitanException e) {
              // TODO: handle exceptions
+			graph.stopTransaction(Conclusion.FAILURE);
 			log.info("SwitchStorage:deletePort dpid:{} port:{} failed", dpid, port);
 		}	
 	}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
index 12ed505..8cf1982 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowManager.java
@@ -1,10 +1,22 @@
 package net.floodlightcontroller.flowcache;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.INetMapStorage;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
+import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
@@ -13,20 +25,178 @@
 import net.floodlightcontroller.flowcache.web.FlowWebRoutable;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.util.CallerId;
+import net.floodlightcontroller.util.DataPath;
+import net.floodlightcontroller.util.Dpid;
 import net.floodlightcontroller.util.DataPathEndpoints;
+import net.floodlightcontroller.util.FlowEntry;
+import net.floodlightcontroller.util.FlowEntryId;
+import net.floodlightcontroller.util.FlowEntrySwitchState;
+import net.floodlightcontroller.util.FlowEntryUserState;
 import net.floodlightcontroller.util.FlowId;
 import net.floodlightcontroller.util.FlowPath;
+import net.floodlightcontroller.util.OFMessageDamper;
+import net.floodlightcontroller.util.Port;
+import net.onrc.onos.util.GraphDBConnection;
+import net.onrc.onos.util.GraphDBConnection.Transaction;
+
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionOutput;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class FlowManager implements IFloodlightModule, IFlowService {
+public class FlowManager implements IFloodlightModule, IFlowService, INetMapStorage {
+
+    public GraphDBConnection conn;
 
     protected IRestApiService restApi;
+    protected IFloodlightProviderService floodlightProvider;
+
+    protected OFMessageDamper messageDamper;
+
+    protected static int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
+    protected static int OFMESSAGE_DAMPER_TIMEOUT = 250;	// ms
+    public static short FLOWMOD_DEFAULT_IDLE_TIMEOUT = 0;	// infinity
+    public static short FLOWMOD_DEFAULT_HARD_TIMEOUT = 0;	// infinite
 
     /** The logger. */
-    private static Logger logger =
-	LoggerFactory.getLogger(FlowManager.class);
+    private static Logger log = LoggerFactory.getLogger(FlowManager.class);
+
+    // The periodic task(s)
+    private final ScheduledExecutorService scheduler =
+	Executors.newScheduledThreadPool(1);
+    final Runnable reader = new Runnable() {
+	    public void run() {
+		// log.debug("Reading Flow Entries from the Network Map...");
+		if (floodlightProvider == null) {
+		    log.debug("FloodlightProvider service not found!");
+		    return;
+		}
+
+		Map<Long, IOFSwitch> mySwitches = floodlightProvider.getSwitches();
+
+		// Fetch all Flow Entries
+		Iterable<IFlowEntry> flowEntries = conn.utils().getAllFlowEntries(conn);
+		for (IFlowEntry flowEntryObj : flowEntries) {
+		    FlowEntryId flowEntryId =
+			new FlowEntryId(flowEntryObj.getFlowEntryId());
+		    String userState = flowEntryObj.getUserState();
+		    String switchState = flowEntryObj.getSwitchState();
+
+		    log.debug("Found Flow Entry {}: ", flowEntryId.toString());
+		    log.debug("User State {}:", userState);
+		    log.debug("Switch State {}:", switchState);
+
+		    if (! switchState.equals("FE_SWITCH_NOT_UPDATED")) {
+			// Ignore the entry: nothing to do
+			continue;
+		    }
+
+		    Dpid dpid = new Dpid(flowEntryObj.getSwitchDpid());
+		    IOFSwitch mySwitch = mySwitches.get(dpid.value());
+		    if (mySwitch == null) {
+			log.debug("Flow Entry ignored: not my switch");
+			continue;
+		    }
+
+		    //
+		    // Create the Open Flow Flow Modification Entry to push
+		    //
+		    OFFlowMod fm =
+			(OFFlowMod) floodlightProvider.getOFMessageFactory()
+			.getMessage(OFType.FLOW_MOD);
+		    long cookie = flowEntryId.value();
+
+		    short flowModCommand = OFFlowMod.OFPFC_ADD;
+		    if (userState.equals("FE_USER_ADD")) {
+			flowModCommand = OFFlowMod.OFPFC_ADD;
+		    } else if (userState.equals("FE_USER_MODIFY")) {
+			flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
+		    } else if (userState.equals("FE_USER_DELETE")) {
+			flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
+		    } else {
+			// Unknown user state. Ignore the entry
+			continue;
+		    }
+
+		    OFMatch match = new OFMatch();
+		    match.setInputPort(flowEntryObj.getInPort());
+
+		    OFActionOutput action = new OFActionOutput();
+		    action.setMaxLength((short)0xffff);
+		    action.setPort(flowEntryObj.getOutPort());
+		    List<OFAction> actions = new ArrayList<OFAction>();
+		    actions.add(action);
+
+		    fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+			.setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+			.setBufferId(OFPacketOut.BUFFER_ID_NONE)
+			.setCookie(cookie)
+			.setCommand(flowModCommand)
+			.setMatch(match)
+			.setActions(actions)
+			.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+		    //
+		    // TODO: Set the following flag
+		    // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
+		    // See method ForwardingBase::pushRoute()
+		    //
+		    try {
+			messageDamper.write(mySwitch, fm, null);
+			mySwitch.flush();
+			flowEntryObj.setSwitchState("FE_SWITCH_UPDATED");
+			if (userState.equals("FE_USER_DELETE")) {
+			    // Delete the entry
+			    IFlowPath flowObj = null;
+			    flowObj = conn.utils().getFlowPathByFlowEntry(conn,
+					flowEntryObj);
+			    if (flowObj != null)
+				log.debug("Found FlowPath to be deleted");
+			    else
+				log.debug("Did not find FlowPath to be deleted");
+			    flowObj.removeFlowEntry(flowEntryObj);
+			    conn.utils().removeFlowEntry(conn, flowEntryObj);
+
+			    // Test whether the last flow entry
+			    Iterable<IFlowEntry> tmpflowEntries =
+				flowObj.getFlowEntries();
+			    boolean found = false;
+			    for (IFlowEntry tmpflowEntryObj : tmpflowEntries) {
+				found = true;
+				break;
+			    }
+			    if (! found) {
+				// Remove the Flow Path as well
+				conn.utils().removeFlowPath(conn, flowObj);
+			    }
+			}
+		    } catch (IOException e) {
+			log.error("Failure writing flow mod from network map", e);
+		    }
+		}
+		conn.endTx(Transaction.COMMIT);
+	    }
+	};
+    final ScheduledFuture<?> readerHandle =
+	scheduler.scheduleAtFixedRate(reader, 3, 3, TimeUnit.SECONDS);
+
+    @Override
+    public void init(String conf) {
+	conn = GraphDBConnection.getInstance(conf);
+    }
+
+    public void finalize() {
+	close();
+    }
+
+    @Override
+    public void close() {
+	conn.close();
+    }
 
     @Override
     public Collection<Class<? extends IFloodlightService>> getModuleServices() {
@@ -52,6 +222,7 @@
                                                     getModuleDependencies() {
 	Collection<Class<? extends IFloodlightService>> l =
 	    new ArrayList<Class<? extends IFloodlightService>>();
+	l.add(IFloodlightProviderService.class);
 	l.add(IRestApiService.class);
         return l;
     }
@@ -59,7 +230,14 @@
     @Override
     public void init(FloodlightModuleContext context)
 	throws FloodlightModuleException {
+	floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
 	restApi = context.getServiceImpl(IRestApiService.class);
+	messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
+					    EnumSet.of(OFType.FLOW_MOD),
+					    OFMESSAGE_DAMPER_TIMEOUT);
+	// TODO: An ugly hack!
+	String conf = "/tmp/cassandra.titan";
+	this.init(conf);
     }
 
     @Override
@@ -79,7 +257,135 @@
      */
     @Override
     public boolean addFlow(FlowPath flowPath, FlowId flowId) {
-	// TODO
+
+	//
+	// Assign the FlowEntry IDs
+	// TODO: This is an ugly hack!
+	// The Flow Entry IDs are set to 1000*FlowId + Index
+	//
+	int i = 1;
+	for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
+	    long id = flowPath.flowId().value() * 1000 + i;
+	    ++i;
+	    flowEntry.setFlowEntryId(new FlowEntryId(id));
+	}
+
+	IFlowPath flowObj = null;
+	try {
+	    if ((flowObj = conn.utils().searchFlowPath(conn, flowPath.flowId()))
+		!= null) {
+		log.debug("Adding FlowPath with FlowId {}: found existing FlowPath",
+			  flowPath.flowId().toString());
+	    } else {
+		flowObj = conn.utils().newFlowPath(conn);
+		log.debug("Adding FlowPath with FlowId {}: creating new FlowPath",
+			  flowPath.flowId().toString());
+	    }
+	} catch (Exception e) {
+	    // TODO: handle exceptions
+	    conn.endTx(Transaction.ROLLBACK);
+	    log.error(":addFlow FlowId:{} failed",
+		      flowPath.flowId().toString());
+	}
+	if (flowObj == null)
+	    return false;
+
+	//
+	// Set the Flow key:
+	// - flowId
+	//
+	flowObj.setFlowId(flowPath.flowId().toString());
+	flowObj.setType("flow");
+
+	//
+	// Set the Flow attributes:
+	// - flowPath.installerId()
+	// - flowPath.dataPath().srcPort()
+	// - flowPath.dataPath().dstPort()
+	//
+	flowObj.setInstallerId(flowPath.installerId().toString());
+	flowObj.setSrcSwitch(flowPath.dataPath().srcPort().dpid().toString());
+	flowObj.setSrcPort(flowPath.dataPath().srcPort().port().value());
+	flowObj.setDstSwitch(flowPath.dataPath().dstPort().dpid().toString());
+	flowObj.setDstPort(flowPath.dataPath().dstPort().port().value());
+
+	// Flow edges:
+	//   HeadFE
+
+
+	//
+	// Flow Entries:
+	// flowPath.dataPath().flowEntries()
+	//
+	for (FlowEntry flowEntry : flowPath.dataPath().flowEntries()) {
+	    IFlowEntry flowEntryObj = null;
+	    boolean found = false;
+	    try {
+		if ((flowEntryObj = conn.utils().searchFlowEntry(conn, flowEntry.flowEntryId())) != null) {
+		    log.debug("Adding FlowEntry with FlowEntryId {}: found existing FlowEntry",
+			      flowEntry.flowEntryId().toString());
+		    found = true;
+		} else {
+		    flowEntryObj = conn.utils().newFlowEntry(conn);
+		    log.debug("Adding FlowEntry with FlowEntryId {}: creating new FlowEntry",
+			      flowEntry.flowEntryId().toString());
+		}
+	    } catch (Exception e) {
+		// TODO: handle exceptions
+		conn.endTx(Transaction.ROLLBACK);
+		log.error(":addFlow FlowEntryId:{} failed",
+			  flowEntry.flowEntryId().toString());
+	    }
+	    if (flowEntryObj == null)
+		return false;
+
+	    //
+	    // Set the Flow Entry key:
+	    // - flowEntry.flowEntryId()
+	    //
+	    flowEntryObj.setFlowEntryId(flowEntry.flowEntryId().toString());
+	    flowEntryObj.setType("flow_entry");
+
+	    // 
+	    // Set the Flow Entry attributes:
+	    // - flowEntry.flowEntryMatch()
+	    // - flowEntry.flowEntryActions()
+	    // - flowEntry.dpid()
+	    // - flowEntry.inPort()
+	    // - flowEntry.outPort()
+	    // - flowEntry.flowEntryUserState()
+	    // - flowEntry.flowEntrySwitchState()
+	    // - flowEntry.flowEntryErrorState()
+	    //
+	    flowEntryObj.setSwitchDpid(flowEntry.dpid().toString());
+	    flowEntryObj.setInPort(flowEntry.inPort().value());
+	    flowEntryObj.setOutPort(flowEntry.outPort().value());
+	    // TODO: Hacks with hard-coded state names!
+	    if (found)
+		flowEntryObj.setUserState("FE_USER_MODIFY");
+	    else
+		flowEntryObj.setUserState("FE_USER_ADD");
+	    flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
+	    //
+	    // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
+	    // and FlowEntryErrorState.
+	    //
+
+	    // Flow Entries edges:
+	    //   Flow
+	    //   NextFE
+	    //   InPort
+	    //   OutPort
+	    //   Switch
+	    if (! found)
+		flowObj.addFlowEntry(flowEntryObj);
+	}
+	conn.endTx(Transaction.COMMIT);
+
+	//
+	// TODO: We need a proper Flow ID allocation mechanism.
+	//
+	flowId.setValue(flowPath.flowId().value());
 	return true;
     }
 
@@ -91,7 +397,46 @@
      */
     @Override
     public boolean deleteFlow(FlowId flowId) {
-	// TODO
+	IFlowPath flowObj = null;
+	//
+	// We just mark the entries for deletion,
+	// and let the switches remove each individual entry after
+	// it has been removed from the switches.
+	//
+	try {
+	    if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
+		!= null) {
+		log.debug("Deleting FlowPath with FlowId {}: found existing FlowPath",
+			  flowId.toString());
+	    } else {
+		log.debug("Deleting FlowPath with FlowId {}:  FlowPath not found",
+			  flowId.toString());
+	    }
+	} catch (Exception e) {
+	    // TODO: handle exceptions
+	    conn.endTx(Transaction.ROLLBACK);
+	    log.error(":deleteFlow FlowId:{} failed", flowId.toString());
+	}
+	if (flowObj == null)
+	    return true;		// OK: No such flow
+
+	//
+	// Find and mark for deletion all Flow Entries
+	//
+	Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
+	boolean empty = true;	// TODO: an ugly hack
+	for (IFlowEntry flowEntryObj : flowEntries) {
+	    empty = false;
+	    // flowObj.removeFlowEntry(flowEntryObj);
+	    // conn.utils().removeFlowEntry(conn, flowEntryObj);
+	    flowEntryObj.setUserState("FE_USER_DELETE");
+	    flowEntryObj.setSwitchState("FE_SWITCH_NOT_UPDATED");
+	}
+	// Remove from the database empty flows
+	if (empty)
+	    conn.utils().removeFlowPath(conn, flowObj);
+	conn.endTx(Transaction.COMMIT);
+
 	return true;
     }
 
@@ -99,51 +444,215 @@
      * Get a previously added flow.
      *
      * @param flowId the Flow ID of the flow to get.
-     * @param flowPath the return-by-reference flow path.
-     * @return true on success, otherwise false.
+     * @return the Flow Path if found, otherwise null.
      */
     @Override
-    public boolean getFlow(FlowId flowId, FlowPath flowPath) {
-	// TODO
-	return true;
+    public FlowPath getFlow(FlowId flowId) {
+	IFlowPath flowObj = null;
+	try {
+	    if ((flowObj = conn.utils().searchFlowPath(conn, flowId))
+		!= null) {
+		log.debug("Get FlowPath with FlowId {}: found existing FlowPath",
+			  flowId.toString());
+	    } else {
+		log.debug("Get FlowPath with FlowId {}:  FlowPath not found",
+			  flowId.toString());
+	    }
+	} catch (Exception e) {
+	    // TODO: handle exceptions
+	    conn.endTx(Transaction.ROLLBACK);
+	    log.error(":getFlow FlowId:{} failed", flowId.toString());
+	}
+	if (flowObj == null)
+	    return null;		// Flow not found
+
+	//
+	// Extract the Flow state
+	//
+	FlowPath flowPath = extractFlowPath(flowObj);
+	conn.endTx(Transaction.COMMIT);
+
+	return flowPath;
     }
 
     /**
-     * Get a previously added flow by a specific installer for given
+     * Get all previously added flows by a specific installer for a given
      * data path endpoints.
      *
      * @param installerId the Caller ID of the installer of the flow to get.
      * @param dataPathEndpoints the data path endpoints of the flow to get.
-     * @param flowPath the return-by-reference flow path.
-     * @return true on success, otherwise false.
+     * @return the Flow Paths if found, otherwise null.
      */
     @Override
-    public boolean getFlow(CallerId installerId,
-			   DataPathEndpoints dataPathEndpoints,
-			   FlowPath flowPath) {
-	// TODO
-	return true;
+    public ArrayList<FlowPath> getAllFlows(CallerId installerId,
+					   DataPathEndpoints dataPathEndpoints) {
+	//
+	// 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> allFlows = getAllFlows();
+
+	if (allFlows == null) {
+	    log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
+	    return null;
+	}
+
+	ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
+	for (FlowPath flow : allFlows) {
+	    //
+	    // TODO: String-based comparison is sub-optimal.
+	    // We are using it for now to save us the extra work of
+	    // implementing the "equals()" and "hashCode()" methods.
+	    //
+	    if (! flow.installerId().toString().equals(installerId.toString()))
+		continue;
+	    if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
+		continue;
+	    }
+	    if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
+		continue;
+	    }
+	    flowPaths.add(flow);
+	}
+
+	if (flowPaths.isEmpty()) {
+	    log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: no FlowPaths found", installerId, dataPathEndpoints);
+	    flowPaths = null;
+	} else {
+	    log.debug("Get FlowPaths for installerId{} and dataPathEndpoints{}: FlowPaths are found", installerId, dataPathEndpoints);
+	}
+
+	return flowPaths;
     }
 
     /**
      * Get all installed flows by all installers for given data path endpoints.
      *
      * @param dataPathEndpoints the data path endpoints of the flows to get.
-     * @param flowPaths the return-by-reference list of flows.
+     * @return the Flow Paths if found, otherwise null.
      */
     @Override
-    public void getAllFlows(DataPathEndpoints dataPathEndpoints,
-			    ArrayList<FlowPath> flowPaths) {
-	// TODO
+    public ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints) {
+	//
+	// 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> allFlows = getAllFlows();
+
+	if (allFlows == null) {
+	    log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
+	    return null;
+	}
+
+	ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
+	for (FlowPath flow : allFlows) {
+	    //
+	    // TODO: String-based comparison is sub-optimal.
+	    // We are using it for now to save us the extra work of
+	    // implementing the "equals()" and "hashCode()" methods.
+	    //
+	    if (! flow.dataPath().srcPort().toString().equals(dataPathEndpoints.srcPort().toString())) {
+		continue;
+	    }
+	    if (! flow.dataPath().dstPort().toString().equals(dataPathEndpoints.dstPort().toString())) {
+		continue;
+	    }
+	    flowPaths.add(flow);
+	}
+
+	if (flowPaths.isEmpty()) {
+	    log.debug("Get FlowPaths for dataPathEndpoints{}: no FlowPaths found", dataPathEndpoints);
+	    flowPaths = null;
+	} else {
+	    log.debug("Get FlowPaths for dataPathEndpoints{}: FlowPaths are found", dataPathEndpoints);
+	}
+
+	return flowPaths;
     }
 
     /**
      * Get all installed flows by all installers.
      *
-     * @param flowPaths the return-by-reference list of flows.
+     * @return the Flow Paths if found, otherwise null.
      */
     @Override
-    public void getAllFlows(ArrayList<FlowPath> flowPaths) {
-	// TODO
+    public ArrayList<FlowPath> getAllFlows() {
+	Iterable<IFlowPath> flowPathsObj = null;
+
+	try {
+	    if ((flowPathsObj = conn.utils().getAllFlowPaths(conn)) != null) {
+		log.debug("Get all FlowPaths: found FlowPaths");
+	    } else {
+		log.debug("Get all FlowPaths: no FlowPaths found");
+	    }
+	} catch (Exception e) {
+	    // TODO: handle exceptions
+	    conn.endTx(Transaction.ROLLBACK);
+	    log.error(":getAllFlowPaths failed");
+	}
+	if ((flowPathsObj == null) || (flowPathsObj.iterator().hasNext() == false))
+	    return null;	// No Flows found
+
+	ArrayList<FlowPath> flowPaths = new ArrayList<FlowPath>();
+	for (IFlowPath flowObj : flowPathsObj) {
+	    //
+	    // Extract the Flow state
+	    //
+	    FlowPath flowPath = extractFlowPath(flowObj);
+	    flowPaths.add(flowPath);
+	}
+
+	conn.endTx(Transaction.COMMIT);
+
+	return flowPaths;
+    }
+
+    /**
+     * Extract Flow Path State from a Titan Database Object @ref IFlowPath.
+     *
+     * @param flowObj the object to extract the Flow Path State from.
+     * @return the extracted Flow Path State.
+     */
+    private FlowPath extractFlowPath(IFlowPath flowObj) {
+	FlowPath flowPath = new FlowPath();
+
+	//
+	// Extract the Flow state
+	//
+	flowPath.setFlowId(new FlowId(flowObj.getFlowId()));
+	flowPath.setInstallerId(new CallerId(flowObj.getInstallerId()));
+	flowPath.dataPath().srcPort().setDpid(new Dpid(flowObj.getSrcSwitch()));
+	flowPath.dataPath().srcPort().setPort(new Port(flowObj.getSrcPort()));
+	flowPath.dataPath().dstPort().setDpid(new Dpid(flowObj.getDstSwitch()));
+	flowPath.dataPath().dstPort().setPort(new Port(flowObj.getDstPort()));
+
+	//
+	// Extract all Flow Entries
+	//
+	Iterable<IFlowEntry> flowEntries = flowObj.getFlowEntries();
+	for (IFlowEntry flowEntryObj : flowEntries) {
+	    FlowEntry flowEntry = new FlowEntry();
+	    flowEntry.setFlowEntryId(new FlowEntryId(flowEntryObj.getFlowEntryId()));
+	    flowEntry.setDpid(new Dpid(flowEntryObj.getSwitchDpid()));
+	    flowEntry.setInPort(new Port(flowEntryObj.getInPort()));
+	    flowEntry.setOutPort(new Port(flowEntryObj.getOutPort()));
+	    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.
+	    //
+	    flowPath.dataPath().flowEntries().add(flowEntry);
+	}
+
+	return flowPath;
     }
 }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
index 956caab..b159661 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/IFlowService.java
@@ -36,37 +36,33 @@
      * Get a previously added flow.
      *
      * @param flowId the Flow ID of the flow to get.
-     * @param flowPath the return-by-reference flow path.
-     * @return true on success, otherwise false.
+     * @return the Flow Path if found, otherwise null.
      */
-    boolean getFlow(FlowId flowId, FlowPath flowPath);
+    FlowPath getFlow(FlowId flowId);
 
     /**
-     * Get a previously added flow by a specific installer for given
+     * Get all previously added flows by a specific installer for a given
      * data path endpoints.
      *
      * @param installerId the Caller ID of the installer of the flow to get.
      * @param dataPathEndpoints the data path endpoints of the flow to get.
-     * @param flowPath the return-by-reference flow path.
-     * @return true on success, otherwise false.
+     * @return the Flow Paths if found, otherwise null.
      */
-    boolean getFlow(CallerId installerId,
-		    DataPathEndpoints dataPathEndpoints,
-		    FlowPath flowPath);
+    ArrayList<FlowPath> getAllFlows(CallerId installerId,
+				 DataPathEndpoints dataPathEndpoints);
 
     /**
      * Get all installed flows by all installers for given data path endpoints.
      *
      * @param dataPathEndpoints the data path endpoints of the flows to get.
-     * @param flowPaths the return-by-reference list of flows.
+     * @return the Flow Paths if found, otherwise null.
      */
-    void getAllFlows(DataPathEndpoints dataPathEndpoints,
-		     ArrayList<FlowPath> flowPaths);
+    ArrayList<FlowPath> getAllFlows(DataPathEndpoints dataPathEndpoints);
 
     /**
      * Get all installed flows by all installers.
      *
-     * @param flowPaths the return-by-reference list of flows.
+     * @return the Flow Paths if found, otherwise null.
      */
-    void getAllFlows(ArrayList<FlowPath> flowPaths);
+    ArrayList<FlowPath> getAllFlows();
 }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
index feb43d3..cdccae1 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/AddFlowResource.java
@@ -10,7 +10,7 @@
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.map.JsonMappingException;
 import org.codehaus.jackson.map.ObjectMapper;
-import org.restlet.resource.Get;
+import org.restlet.resource.Post;
 import org.restlet.resource.ServerResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -19,8 +19,8 @@
 
     protected static Logger log = LoggerFactory.getLogger(AddFlowResource.class);
 
-    @Get("json")
-    public FlowId retrieve() {
+    @Post("json")
+    public FlowId store(String flowJson) {
 	FlowId result = new FlowId();
 
         IFlowService flowService =
@@ -37,7 +37,7 @@
 	// NOTE: The "flow" is specified in JSON format.
 	//
 	ObjectMapper mapper = new ObjectMapper();
-	String flowPathStr = (String) getRequestAttributes().get("flow");
+	String flowPathStr = flowJson;
 	FlowPath flowPath = null;
 	log.debug("Add Flow Path: " + flowPathStr);
 	try {
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
index 0859397..cfd3505 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/FlowWebRoutable.java
@@ -13,11 +13,11 @@
     @Override
     public Restlet getRestlet(Context context) {
         Router router = new Router(context);
-        router.attach("/add/{flow}/json", AddFlowResource.class);
+        router.attach("/add/json", AddFlowResource.class);
         router.attach("/delete/{flow-id}/json", DeleteFlowResource.class);
         router.attach("/get/{flow-id}/json", GetFlowByIdResource.class);
-        router.attach("/get/{installer-id}/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetFlowByInstallerIdResource.class);
-        router.attach("/getall/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByEndpointsResource.class);
+        router.attach("/getall-by-installer-id/{installer-id}/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByInstallerIdResource.class);
+        router.attach("/getall-by-endpoints/{src-dpid}/{src-port}/{dst-dpid}/{dst-port}/json", GetAllFlowsByEndpointsResource.class);
         router.attach("/getall/json", GetAllFlowsResource.class);
         return router;
     }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java
index c485d91..34d79c8 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByEndpointsResource.java
@@ -19,7 +19,7 @@
 
     @Get("json")
     public ArrayList<FlowPath> retrieve() {
-	ArrayList<FlowPath> result = new ArrayList<FlowPath>();
+	ArrayList<FlowPath> result = null;
 
         IFlowService flowService =
                 (IFlowService)getContext().getAttributes().
@@ -48,7 +48,7 @@
 	DataPathEndpoints dataPathEndpoints =
 	    new DataPathEndpoints(srcSwitchPort, dstSwitchPort);
 
-	flowService.getAllFlows(dataPathEndpoints, result);
+	result = flowService.getAllFlows(dataPathEndpoints);
 
         return result;
     }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByInstallerIdResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByInstallerIdResource.java
similarity index 80%
rename from src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByInstallerIdResource.java
rename to src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByInstallerIdResource.java
index cb4e6ef..e3043dc 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByInstallerIdResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsByInstallerIdResource.java
@@ -1,5 +1,7 @@
 package net.floodlightcontroller.flowcache.web;
 
+import java.util.ArrayList;
+
 import net.floodlightcontroller.flowcache.IFlowService;
 import net.floodlightcontroller.util.CallerId;
 import net.floodlightcontroller.util.DataPathEndpoints;
@@ -13,12 +15,12 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class GetFlowByInstallerIdResource extends ServerResource {
-    protected static Logger log = LoggerFactory.getLogger(GetFlowByInstallerIdResource.class);
+public class GetAllFlowsByInstallerIdResource extends ServerResource {
+    protected static Logger log = LoggerFactory.getLogger(GetAllFlowsByInstallerIdResource.class);
 
     @Get("json")
-    public FlowPath retrieve() {
-	FlowPath result = null;
+    public ArrayList<FlowPath> retrieve() {
+	ArrayList<FlowPath> result = null;
 
         IFlowService flowService =
                 (IFlowService)getContext().getAttributes().
@@ -36,7 +38,8 @@
         String dstDpidStr = (String) getRequestAttributes().get("dst-dpid");
         String dstPortStr = (String) getRequestAttributes().get("dst-port");
 
-	log.debug("Get Flow By Installer: " + installerIdStr + " Endpoints: " +
+	log.debug("Get All Flow By Installer: " + installerIdStr +
+		  " Endpoints: " +
 		  srcDpidStr + "--" + srcPortStr + "--" +
 		  dstDpidStr + "--" + dstPortStr);
 
@@ -50,7 +53,7 @@
 	DataPathEndpoints dataPathEndpoints =
 	    new DataPathEndpoints(srcSwitchPort, dstSwitchPort);
 
-	flowService.getFlow(installerId, dataPathEndpoints, result);
+	result = flowService.getAllFlows(installerId, dataPathEndpoints);
 
         return result;
     }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java
index deb4d04..92317cf 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetAllFlowsResource.java
@@ -15,7 +15,7 @@
 
     @Get("json")
     public ArrayList<FlowPath> retrieve() {
-	ArrayList<FlowPath> result = new ArrayList<FlowPath>();
+	ArrayList<FlowPath> result = null;
 
         IFlowService flowService =
                 (IFlowService)getContext().getAttributes().
@@ -29,9 +29,7 @@
 	// Extract the arguments
 	log.debug("Get All Flows Endpoints");
 
-	flowService.getAllFlows(result);
-	FlowPath flowPath = new FlowPath();
-	result.add(flowPath);
+	result = flowService.getAllFlows();
 
         return result;
     }
diff --git a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java b/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java
index d5b2730..85d5b7e 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/web/GetFlowByIdResource.java
@@ -31,7 +31,7 @@
 
 	log.debug("Get Flow Id: " + flowIdStr);
 
-	flowService.getFlow(flowId, result);
+	result = flowService.getFlow(flowId);
 
         return result;
     }
diff --git a/src/main/java/net/floodlightcontroller/restclient/RestClient.java b/src/main/java/net/floodlightcontroller/restclient/RestClient.java
new file mode 100644
index 0000000..07eab45
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/restclient/RestClient.java
@@ -0,0 +1,51 @@
+package net.floodlightcontroller.restclient;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class RestClient {
+
+	public static void get (String str) {
+		
+		if (str == null)
+			return;
+	
+		try {
+	 
+			URL url = new URL(str);
+			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+			conn.setRequestMethod("GET"); 
+			conn.setRequestProperty("Accept", "application/json");
+	 
+			if (conn.getResponseCode() != 200) {
+				throw new RuntimeException("Failed : HTTP error code : "
+						+ conn.getResponseCode());
+			}
+	 
+			/* Disable reading the output from the server for now
+			 * 
+			BufferedReader br = new BufferedReader(new InputStreamReader(
+					(conn.getInputStream())));
+	 
+			String output;
+			System.out.println("Output from Server .... \n");
+			while ((output = br.readLine()) != null) {
+				System.out.println(output);
+			}
+			 */
+			
+			conn.disconnect();
+
+		} catch (MalformedURLException e) {
+
+			e.printStackTrace();
+
+		} catch (IOException e) {
+
+			e.printStackTrace();
+
+		}
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
index f979cc7..94e4769 100644
--- a/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
+++ b/src/main/java/net/floodlightcontroller/routing/TopoRouteService.java
@@ -22,6 +22,7 @@
 import org.openflow.util.HexString;
 
 import com.thinkaurelius.titan.core.TitanGraph;
+import com.tinkerpop.blueprints.TransactionalGraph.Conclusion;
 import com.tinkerpop.blueprints.Vertex;
 
 import javax.script.ScriptContext;
@@ -35,7 +36,7 @@
 public class TopoRouteService implements IFloodlightModule, ITopoRouteService {
 
     /** The logger. */
-    private static Logger logger =
+    private static Logger log =
 	LoggerFactory.getLogger(TopoRouteService.class);
 
     @Override
@@ -115,14 +116,18 @@
 
 	// Get the source vertex
 	Iterator<Vertex> iter = titanGraph.getVertices("dpid", dpid_src).iterator();
-	if (! iter.hasNext())
+	if (! iter.hasNext()) {
+	    // titanGraph.stopTransaction(Conclusion.SUCCESS);
 	    return null;		// Source vertex not found
+	}
 	Vertex v_src = iter.next();
 
 	// Get the destination vertex
 	iter = titanGraph.getVertices("dpid", dpid_dest).iterator();
-	if (! iter.hasNext())
+	if (! iter.hasNext()) {
+	    // titanGraph.stopTransaction(Conclusion.SUCCESS);
 	    return null;		// Destination vertex not found
+	}
 	Vertex v_dest = iter.next();
 
 	//
@@ -138,6 +143,7 @@
 	    flowEntry.setInPort(src.port());
 	    flowEntry.setOutPort(dest.port());
 	    result_data_path.flowEntries().add(flowEntry);
+	    // titanGraph.stopTransaction(Conclusion.SUCCESS);
 	    return result_data_path;
 	}
 
@@ -156,6 +162,7 @@
 	    engine.eval(gremlin);
 	} catch (ScriptException e) {
 	    System.err.println("Caught ScriptException running Gremlin script: " + e.getMessage());
+	    // titanGraph.stopTransaction(Conclusion.SUCCESS);
 	    return null;
 	}
 
@@ -224,6 +231,7 @@
 		result_data_path.flowEntries().add(flowEntry);
 	    }
 	}
+	// titanGraph.stopTransaction(Conclusion.SUCCESS);
 	if (result_data_path.flowEntries().size() > 0)
 	    return result_data_path;
 
diff --git a/src/main/java/net/floodlightcontroller/util/DataPath.java b/src/main/java/net/floodlightcontroller/util/DataPath.java
index 71e0a2f..b2dded6 100644
--- a/src/main/java/net/floodlightcontroller/util/DataPath.java
+++ b/src/main/java/net/floodlightcontroller/util/DataPath.java
@@ -19,6 +19,8 @@
      * Default constructor.
      */
     public DataPath() {
+	srcPort = new SwitchPort();
+	dstPort = new SwitchPort();
 	flowEntries = new ArrayList<FlowEntry>();
     }
 
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntry.java b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
index dfb8f82..64c32b4 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntry.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntry.java
@@ -1,36 +1,21 @@
 package net.floodlightcontroller.util;
 
+import java.util.ArrayList;
+
 import net.floodlightcontroller.util.Dpid;
-import net.floodlightcontroller.util.FlowEntryActions;
+import net.floodlightcontroller.util.FlowEntryAction;
 import net.floodlightcontroller.util.FlowEntryId;
 import net.floodlightcontroller.util.FlowEntryMatch;
+import net.floodlightcontroller.util.FlowEntrySwitchState;
+import net.floodlightcontroller.util.FlowEntryUserState;
 import net.floodlightcontroller.util.Port;
 
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.IPv4;
+
 import org.codehaus.jackson.annotate.JsonProperty;
 
 /**
- * The Flow Entry state as set by the user (via the ONOS API).
- */
-enum FlowEntryUserState {
-	FE_USER_UNKNOWN,		// Initialization value: state unknown
-	FE_USER_ADD,			// Flow entry that is added
-	FE_USER_MODIFY,			// Flow entry that is modified
-	FE_USER_DELETE			// Flow entry that is deleted
-}
-
-/**
- * The Flow Entry state as set by the controller.
- */
-enum FlowEntrySwitchState {
-	FE_SWITCH_UNKNOWN,		// Initialization value: state unknown
-	FE_SWITCH_NOT_UPDATED,		// Switch not updated with this entry
-	FE_SWITCH_UPDATE_IN_PROGRESS,	// Switch update in progress
-	FE_SWITCH_UPDATED,		// Switch updated with this entry
-	FE_SWITCH_UPDATE_FAILED	// Error updating the switch with this entry
-}
-
-
-/**
  * The class representing the Flow Entry.
  *
  * NOTE: The specification is incomplete. E.g., the entry needs to
@@ -39,7 +24,7 @@
 public class FlowEntry {
     private FlowEntryId flowEntryId;		// The Flow Entry ID
     private FlowEntryMatch flowEntryMatch;	// The Flow Entry Match
-    private FlowEntryActions flowEntryActions;	// The Flow Entry Actions
+    private ArrayList<FlowEntryAction> flowEntryActions; // The Flow Entry Actions
     private Dpid dpid;				// The Switch DPID
     private Port inPort;			// The Switch incoming port
     private Port outPort;			// The Switch outgoing port
@@ -52,6 +37,85 @@
      * Default constructor.
      */
     public FlowEntry() {
+	// TODO: Test code
+	/*
+	MACAddress mac = MACAddress.valueOf("01:02:03:04:05:06");
+	IPv4 ipv4 = new IPv4("1.2.3.4");
+	IPv4Net ipv4net = new IPv4Net("5.6.7.0/24");
+
+	flowEntryMatch = new FlowEntryMatch();
+	flowEntryMatch.enableInPort(new Port((short)10));
+	flowEntryMatch.enableSrcMac(mac);
+	flowEntryMatch.enableDstMac(mac);
+	flowEntryMatch.enableVlanId((short)20);
+	flowEntryMatch.enableVlanPriority((byte)30);
+	flowEntryMatch.enableEthernetFrameType((short)40);
+	flowEntryMatch.enableIpToS((byte)50);
+	flowEntryMatch.enableIpProto((byte)60);
+	flowEntryMatch.enableSrcIPv4Net(ipv4net);
+	flowEntryMatch.enableDstIPv4Net(ipv4net);
+	flowEntryMatch.enableSrcTcpUdpPort((short)70);
+	flowEntryMatch.enableDstTcpUdpPort((short)80);
+
+	FlowEntryAction action = null;
+	ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
+
+	action = new FlowEntryAction();
+	action.setActionOutput(new Port((short)12));
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionOutputToController((short)13);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionSetVlanId((short)14);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionSetVlanPriority((byte)15);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionStripVlan(true);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionSetEthernetSrcAddr(mac);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionSetEthernetDstAddr(mac);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionSetIPv4SrcAddr(ipv4);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionSetIPv4DstAddr(ipv4);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionSetIpToS((byte)16);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionSetTcpUdpSrcPort((short)17);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionSetTcpUdpDstPort((short)18);
+	actions.add(action);
+
+	action = new FlowEntryAction();
+	action.setActionEnqueue(new Port((short)19), 20);
+	actions.add(action);
+
+	setFlowEntryActions(actions);
+	*/
+
+
 	flowEntryUserState = FlowEntryUserState.FE_USER_UNKNOWN;
 	flowEntrySwitchState = FlowEntrySwitchState.FE_SWITCH_UNKNOWN;
     }
@@ -98,7 +162,9 @@
      * @return the Flow Entry Actions.
      */
     @JsonProperty("flowEntryActions")
-    public FlowEntryActions flowEntryActions() { return flowEntryActions; }
+    public ArrayList<FlowEntryAction> flowEntryActions() {
+	return flowEntryActions;
+    }
 
     /**
      * Set the Flow Entry Actions.
@@ -106,7 +172,7 @@
      * @param flowEntryActions the Flow Entry Actions to set.
      */
     @JsonProperty("flowEntryActions")
-    public void setFlowEntryActions(FlowEntryActions flowEntryActions) {
+    public void setFlowEntryActions(ArrayList<FlowEntryAction> flowEntryActions) {
 	this.flowEntryActions = flowEntryActions;
     }
 
@@ -234,7 +300,8 @@
      * Convert the flow entry to a string.
      *
      * The string has the following form:
-     *  [flowEntryId=XXX flowEntryMatch=XXX flowEntryActions=XXX dpid=XXX
+     *  [flowEntryId=XXX flowEntryMatch=XXX flowEntryAction=XXX
+     *   flowEntryAction=XXX flowEntryAction=XXX dpid=XXX
      *   inPort=XXX outPort=XXX flowEntryUserState=XXX flowEntrySwitchState=XXX
      *   flowEntryErrorState=XXX]
      * @return the flow entry as a string.
@@ -243,7 +310,9 @@
     public String toString() {
 	String ret = "[flowEntryId=" + this.flowEntryId.toString();
 	ret += " flowEntryMatch=" + this.flowEntryMatch.toString();
-	ret += " flowEntryActions=" + this.flowEntryActions.toString();
+	for (FlowEntryAction fa : flowEntryActions) {
+	    ret += " flowEntryAction=" + fa.toString();
+	}
 	ret += " dpid=" + this.dpid.toString();
 	ret += " inPort=" + this.inPort.toString();
 	ret += " outPort=" + this.outPort.toString();
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryAction.java b/src/main/java/net/floodlightcontroller/util/FlowEntryAction.java
new file mode 100644
index 0000000..304bb5c
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryAction.java
@@ -0,0 +1,820 @@
+package net.floodlightcontroller.util;
+
+import net.floodlightcontroller.util.IPv4;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.Port;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing a single Flow Entry action.
+ *
+ * A set of Flow Entry actions need to be applied to each packet.
+ */
+public class FlowEntryAction {
+    /**
+     * Special action values.
+     *
+     * Those values are taken as-is from the OpenFlow-v1.0.0 specification
+     * (pp 21-22).
+     */
+    public enum ActionValues {
+	ACTION_OUTPUT		((short)0x0),	// Output to switch port
+	ACTION_SET_VLAN_VID	((short)0x1),	// Set the 802.1q VLAN id
+	ACTION_SET_VLAN_PCP	((short)0x2),	// Set the 802.1q priority
+	ACTION_STRIP_VLAN	((short)0x3),	// Strip the 802.1q header
+	ACTION_SET_DL_SRC	((short)0x4),	// Ethernet source address
+	ACTION_SET_DL_DST	((short)0x5),	// Ethernet destination address
+	ACTION_SET_NW_SRC	((short)0x6),	// IP source address
+	ACTION_SET_NW_DST	((short)0x7),	// IP destination address
+	ACTION_SET_NW_TOS	((short)0x8),	// IP ToS (DSCP field, 6 bits)
+	ACTION_SET_TP_SRC	((short)0x9),	// TCP/UDP source port
+	ACTION_SET_TP_DST	((short)0xa),	// TCP/UDP destination port
+	ACTION_ENQUEUE		((short)0xb),	// Output to queue on port
+	ACTION_VENDOR		((short)0xffff); // Vendor-specific
+
+	private final short value;	// The value
+
+	/**
+	 * Constructor for a given value.
+	 *
+	 * @param value the value to use for the initialization.
+	 */
+	private ActionValues(short value) {
+	    this.value = value;
+	}
+    }
+
+    /**
+     * Action structure for ACTION_OUTPUT: Output to switch port.
+     */
+    public class ActionOutput {
+	private Port port;	// Output port
+	private short maxLen;	// Max. length (in bytes) to send to controller
+				// if the port is set to PORT_CONTROLLER
+
+	/**
+	 * Constructor for a given output port and maximum length.
+	 *
+	 * @param port the output port to set.
+	 * @param maxLen the maximum length (in bytes) to send to controller
+	 * if the port is set to PORT_CONTROLLER.
+	 */
+	public ActionOutput(Port port, short maxLen) {
+	    this.port = port;
+	    this.maxLen = maxLen;
+	}
+
+	/**
+	 * Get the output port.
+	 *
+	 * @return the output port.
+	 */
+	@JsonProperty("port")
+	public Port port() {
+	    return this.port;
+	}
+
+	/**
+	 * Get the maximum length (in bytes) to send to controller if the
+	 * port is set to PORT_CONTROLLER.
+	 *
+	 * @return the maximum length (in bytes) to send to controller if the
+	 * port is set to PORT_CONTROLLER.
+	 */
+	@JsonProperty("maxLen")
+	public short maxLen() {
+	    return this.maxLen;
+	}
+
+	/**
+	 * Convert the action to a string.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX maxLen=XXX]
+	 *
+	 * @return the action as a string.
+	 */
+	@Override
+	public String toString() {
+	    String ret = "[";
+	    ret += "port=" + port.toString();
+	    ret += " maxLen=" + maxLen;
+	    ret += "]";
+
+	    return ret;
+	}
+    }
+
+    /**
+     * Action structure for ACTION_SET_VLAN_VID: Set the 802.1q VLAN id
+     */
+    public class ActionSetVlanId {
+	private short vlanId;		// The VLAN ID to set
+
+	/**
+	 * Constructor for a given VLAN ID.
+	 *
+	 * @param vlanId the VLAN ID to set.
+	 */
+	ActionSetVlanId(short vlanId) {
+	    this.vlanId = vlanId;
+	}
+
+	/**
+	 * Get the VLAN ID.
+	 *
+	 * @return the VLAN ID.
+	 */
+	@JsonProperty("vlanId")
+	public short vlanId() {
+	    return this.vlanId;
+	}
+
+	/**
+	 * Convert the action to a string.
+	 *
+	 * The string has the following form:
+	 *  [vlanId=XXX]
+	 *
+	 * @return the action as a string.
+	 */
+	@Override
+	public String toString() {
+	    String ret = "[";
+	    ret += "vlanId=" + this.vlanId;
+	    ret += "]";
+
+	    return ret;
+	}
+    }
+
+    /**
+     * Action structure for ACTION_SET_VLAN_PCP: Set the 802.1q priority
+     */
+    public class ActionSetVlanPriority {
+	private byte vlanPriority;	// The VLAN priority to set
+
+	/**
+	 * Constructor for a given VLAN priority.
+	 *
+	 * @param vlanPriority the VLAN priority to set.
+	 */
+	ActionSetVlanPriority(byte vlanPriority) {
+	    this.vlanPriority = vlanPriority;
+	}
+
+	/**
+	 * Get the VLAN priority.
+	 *
+	 * @return the VLAN priority.
+	 */
+	@JsonProperty("vlanPriority")
+	public byte vlanPriority() {
+	    return this.vlanPriority;
+	}
+
+	/**
+	 * Convert the action to a string.
+	 *
+	 * The string has the following form:
+	 *  [vlanPriority=XXX]
+	 *
+	 * @return the action as a string.
+	 */
+	@Override
+	public String toString() {
+	    String ret = "[";
+	    ret += "vlanPriority=" + this.vlanPriority;
+	    ret += "]";
+
+	    return ret;
+	}
+    }
+
+    /**
+     * Action structure for ACTION_STRIP_VLAN: Strip the 802.1q header
+     */
+    public class ActionStripVlan {
+	private boolean stripVlan;	// If true, strip the VLAN header
+
+	/**
+	 * Constructor for a given boolean flag.
+	 *
+	 * @param stripVlan if true, strip the VLAN header.
+	 */
+	ActionStripVlan(boolean stripVlan) {
+	    this.stripVlan = stripVlan;
+	}
+
+	/**
+	 * Get the boolean flag whether the VLAN header should be stripped.
+	 *
+	 * @return the boolean flag whether the VLAN header should be stripped.
+	 */
+	@JsonProperty("stripVlan")
+	public boolean stripVlan() {
+	    return this.stripVlan;
+	}
+
+	/**
+	 * Convert the action to a string.
+	 *
+	 * The string has the following form:
+	 *  [stripVlan=XXX]
+	 *
+	 * @return the action as a string.
+	 */
+	@Override
+	public String toString() {
+	    String ret = "[";
+	    ret += "stripVlan=" + this.stripVlan;
+	    ret += "]";
+
+	    return ret;
+	}
+    }
+
+    /**
+     * Action structure for ACTION_SET_DL_SRC and ACTION_SET_DL_DST:
+     * Set the Ethernet source/destination address.
+     */
+    public class ActionSetEthernetAddr {
+	private MACAddress addr;	// The MAC address to set
+
+	/**
+	 * Constructor for a given MAC address.
+	 *
+	 * @param addr the MAC address to set.
+	 */
+	ActionSetEthernetAddr(MACAddress addr) {
+	    this.addr = addr;
+	}
+
+	/**
+	 * Get the MAC address.
+	 *
+	 * @return the MAC address.
+	 */
+	@JsonProperty("addr")
+	public MACAddress addr() {
+	    return this.addr;
+	}
+
+	/**
+	 * Convert the action to a string.
+	 *
+	 * The string has the following form:
+	 *  [addr=XXX]
+	 *
+	 * @return the action as a string.
+	 */
+	@Override
+	public String toString() {
+	    String ret = "[";
+	    ret += "addr=" + addr.toString();
+	    ret += "]";
+
+	    return ret;
+	}
+    }
+
+    /**
+     * Action structure for ACTION_SET_NW_SRC and ACTION_SET_NW_DST:
+     * Set the IPv4 source/destination address.
+     */
+    public class ActionSetIPv4Addr {
+	private IPv4 addr;		// The IPv4 address to set
+
+	/**
+	 * Constructor for a given IPv4 address.
+	 *
+	 * @param addr the IPv4 address to set.
+	 */
+	ActionSetIPv4Addr(IPv4 addr) {
+	    this.addr = addr;
+	}
+
+	/**
+	 * Get the IPv4 address.
+	 *
+	 * @return the IPv4 address.
+	 */
+	@JsonProperty("addr")
+	public IPv4 addr() {
+	    return this.addr;
+	}
+
+	/**
+	 * Convert the action to a string.
+	 *
+	 * The string has the following form:
+	 *  [addr=XXX]
+	 *
+	 * @return the action as a string.
+	 */
+	@Override
+	public String toString() {
+	    String ret = "[";
+	    ret += "addr=" + addr.toString();
+	    ret += "]";
+
+	    return ret;
+	}
+    }
+
+    /**
+     * Action structure for ACTION_SET_NW_TOS:
+     * Set the IP ToS (DSCP field, 6 bits).
+     */
+    public class ActionSetIpToS {
+	private byte ipToS;	// The IP ToS to set DSCP field, 6 bits)
+
+	/**
+	 * Constructor for a given IP ToS (DSCP field, 6 bits).
+	 *
+	 * @param ipToS the IP ToS (DSCP field, 6 bits) to set.
+	 */
+	ActionSetIpToS(byte ipToS) {
+	    this.ipToS = ipToS;
+	}
+
+	/**
+	 * Get the IP ToS (DSCP field, 6 bits).
+	 *
+	 * @return the IP ToS (DSCP field, 6 bits).
+	 */
+	@JsonProperty("ipToS")
+	public byte ipToS() {
+	    return this.ipToS;
+	}
+
+	/**
+	 * Convert the action to a string.
+	 *
+	 * The string has the following form:
+	 *  [ipToS=XXX]
+	 *
+	 * @return the action as a string.
+	 */
+	@Override
+	public String toString() {
+	    String ret = "[";
+	    ret += "ipToS=" + ipToS;
+	    ret += "]";
+
+	    return ret;
+	}
+    }
+
+    /**
+     * Action structure for ACTION_SET_TP_SRC and ACTION_SET_TP_DST:
+     * Set the TCP/UDP source/destination port.
+     */
+    public class ActionSetTcpUdpPort {
+	private short port;		// The TCP/UDP port to set
+
+	/**
+	 * Constructor for a given TCP/UDP port.
+	 *
+	 * @param port the TCP/UDP port to set.
+	 */
+	ActionSetTcpUdpPort(short port) {
+	    this.port = port;
+	}
+
+	/**
+	 * Get the TCP/UDP port.
+	 *
+	 * @return the TCP/UDP port.
+	 */
+	@JsonProperty("port")
+	public short port() {
+	    return this.port;
+	}
+
+	/**
+	 * Convert the action to a string.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX]
+	 *
+	 * @return the action as a string.
+	 */
+	@Override
+	public String toString() {
+	    String ret = "[";
+	    ret += "port=" + port;
+	    ret += "]";
+
+	    return ret;
+	}
+    }
+
+    /**
+     * Action structure for ACTION_ENQUEUE: Output to queue on port.
+     */
+    public class ActionEnqueue {
+	private Port port;	// Port that queue belongs. Should
+				// refer to a valid physical port
+				// (i.e. < PORT_MAX) or PORT_IN_PORT
+	private int queueId;	// Where to enqueue the packets
+
+	/**
+	 * Constructor for a given port and queue ID.
+	 *
+	 * @param port the port to set.
+	 * @param queueId the queue ID on the port.
+	 */
+	public ActionEnqueue(Port port, int queueId) {
+	    this.port = port;
+	    this.queueId = queueId;
+	}
+
+	/**
+	 * Get the port.
+	 *
+	 * @return the port.
+	 */
+	@JsonProperty("port")
+	public Port port() {
+	    return this.port;
+	}
+
+	/**
+	 * Get the queue ID.
+	 *
+	 * @return the queue ID.
+	 */
+	@JsonProperty("queueId")
+	public int queueId() {
+	    return this.queueId;
+	}
+
+	/**
+	 * Convert the action to a string.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX queueId=XXX]
+	 *
+	 * @return the action as a string.
+	 */
+	@Override
+	public String toString() {
+	    String ret = "[";
+	    ret += "port=" + port.toString();
+	    ret += " queueId=" + queueId;
+	    ret += "]";
+
+	    return ret;
+	}
+    }
+
+    private ActionValues actionType;	// The action type
+
+    //
+    // The actions.
+    // NOTE: Only one action should be set.
+    //
+    private ActionOutput actionOutput;
+    private ActionSetVlanId actionSetVlanId;
+    private ActionSetVlanPriority actionSetVlanPriority;
+    private ActionStripVlan actionStripVlan;
+    private ActionSetEthernetAddr actionSetEthernetSrcAddr;
+    private ActionSetEthernetAddr actionSetEthernetDstAddr;
+    private ActionSetIPv4Addr actionSetIPv4SrcAddr;
+    private ActionSetIPv4Addr actionSetIPv4DstAddr;
+    private ActionSetIpToS actionSetIpToS;
+    private ActionSetTcpUdpPort actionSetTcpUdpSrcPort;
+    private ActionSetTcpUdpPort actionSetTcpUdpDstPort;
+    private ActionEnqueue actionEnqueue;
+
+    /**
+     * Default constructor.
+     */
+    public FlowEntryAction() {
+	actionType = ActionValues.ACTION_VENDOR;	// XXX: Initial value
+    }
+
+    /**
+     * Get the action type.
+     *
+     * @return the action type.
+     */
+    @JsonProperty("actionType")
+    public ActionValues actionType() { return actionType; }
+
+    /**
+     * Get the output action.
+     *
+     * @return the output action.
+     */
+    @JsonProperty("actionOutput")
+    public ActionOutput actionOutput() { return actionOutput; }
+
+    /**
+     * Set the output action on a port.
+     *
+     * @param port the output port to set.
+     */
+    @JsonProperty("actionOutput")
+    public void setActionOutput(Port port) {
+	actionOutput = new ActionOutput(port, (short)0);
+	actionType = ActionValues.ACTION_OUTPUT;
+    }
+
+    /**
+     * Set the output action to controller.
+     *
+     * @param maxLen the maximum length (in bytes) to send to controller.
+     */
+    @JsonProperty("actionOutputToController")
+    public void setActionOutputToController(short maxLen) {
+	Port port = new Port(Port.PortValues.PORT_CONTROLLER);
+	actionOutput = new ActionOutput(port, maxLen);
+	actionType = ActionValues.ACTION_OUTPUT;
+    }
+
+    /**
+     * Get the action to set the VLAN ID.
+     *
+     * @return the action to set the VLAN ID.
+     */
+    @JsonProperty("actionSetVlanId")
+    public ActionSetVlanId actionSetVlanId() { return actionSetVlanId; }
+
+    /**
+     * Set the action to set the VLAN ID.
+     *
+     * @param vlanId the VLAN ID to set.
+     */
+    @JsonProperty("actionSetVlanId")
+    public void setActionSetVlanId(short vlanId) {
+	actionSetVlanId = new ActionSetVlanId(vlanId);
+	actionType = ActionValues.ACTION_SET_VLAN_VID;
+    }
+
+    /**
+     * Get the action to set the VLAN priority.
+     *
+     * @return the action to set the VLAN priority.
+     */
+    @JsonProperty("actionSetVlanPriority")
+    public ActionSetVlanPriority actionSetVlanPriority() {
+	return actionSetVlanPriority;
+    }
+
+    /**
+     * Set the action to set the VLAN priority.
+     *
+     * @param vlanPriority the VLAN priority to set.
+     */
+    @JsonProperty("actionSetVlanPriority")
+    public void setActionSetVlanPriority(byte vlanPriority) {
+	actionSetVlanPriority = new ActionSetVlanPriority(vlanPriority);
+	actionType = ActionValues.ACTION_SET_VLAN_PCP;
+    }
+
+    /**
+     * Get the action to strip the VLAN header.
+     *
+     * @return the action to strip the VLAN header.
+     */
+    @JsonProperty("actionStripVlan")
+    public ActionStripVlan actionStripVlan() {
+	return actionStripVlan;
+    }
+
+    /**
+     * Set the action to strip the VLAN header.
+     *
+     * @param stripVlan if true, strip the VLAN header.
+     */
+    @JsonProperty("actionStripVlan")
+    public void setActionStripVlan(boolean stripVlan) {
+	actionStripVlan = new ActionStripVlan(stripVlan);
+	actionType = ActionValues.ACTION_STRIP_VLAN;
+    }
+
+    /**
+     * Get the action to set the Ethernet source address.
+     *
+     * @return the action to set the Ethernet source address.
+     */
+    @JsonProperty("actionSetEthernetSrcAddr")
+    public ActionSetEthernetAddr actionSetEthernetSrcAddr() {
+	return actionSetEthernetSrcAddr;
+    }
+
+    /**
+     * Set the action to set the Ethernet source address.
+     *
+     * @param addr the MAC address to set as the Ethernet source address.
+     */
+    @JsonProperty("actionSetEthernetSrcAddr")
+    public void setActionSetEthernetSrcAddr(MACAddress addr) {
+	actionSetEthernetSrcAddr = new ActionSetEthernetAddr(addr);
+	actionType = ActionValues.ACTION_SET_DL_SRC;
+    }
+
+    /**
+     * Get the action to set the Ethernet destination address.
+     *
+     * @return the action to set the Ethernet destination address.
+     */
+    @JsonProperty("actionSetEthernetDstAddr")
+    public ActionSetEthernetAddr actionSetEthernetDstAddr() {
+	return actionSetEthernetDstAddr;
+    }
+
+    /**
+     * Set the action to set the Ethernet destination address.
+     *
+     * @param addr the MAC address to set as the Ethernet destination address.
+     */
+    @JsonProperty("actionSetEthernetDstAddr")
+    public void setActionSetEthernetDstAddr(MACAddress addr) {
+	actionSetEthernetDstAddr = new ActionSetEthernetAddr(addr);
+	actionType = ActionValues.ACTION_SET_DL_DST;
+    }
+
+    /**
+     * Get the action to set the IPv4 source address.
+     *
+     * @return the action to set the IPv4 source address.
+     */
+    @JsonProperty("actionSetIPv4SrcAddr")
+    public ActionSetIPv4Addr actionSetIPv4SrcAddr() {
+	return actionSetIPv4SrcAddr;
+    }
+
+    /**
+     * Set the action to set the IPv4 source address.
+     *
+     * @param addr the IPv4 address to set as the IPv4 source address.
+     */
+    @JsonProperty("actionSetIPv4SrcAddr")
+    public void setActionSetIPv4SrcAddr(IPv4 addr) {
+	actionSetIPv4SrcAddr = new ActionSetIPv4Addr(addr);
+	actionType = ActionValues.ACTION_SET_NW_SRC;
+    }
+
+    /**
+     * Get the action to set the IPv4 destination address.
+     *
+     * @return the action to set the IPv4 destination address.
+     */
+    @JsonProperty("actionSetIPv4DstAddr")
+    public ActionSetIPv4Addr actionSetIPv4DstAddr() {
+	return actionSetIPv4DstAddr;
+    }
+
+    /**
+     * Set the action to set the IPv4 destination address.
+     *
+     * @param addr the IPv4 address to set as the IPv4 destination address.
+     */
+    @JsonProperty("actionSetIPv4DstAddr")
+    public void setActionSetIPv4DstAddr(IPv4 addr) {
+	actionSetIPv4DstAddr = new ActionSetIPv4Addr(addr);
+	actionType = ActionValues.ACTION_SET_NW_DST;
+    }
+
+    /**
+     * Get the action to set the IP ToS (DSCP field, 6 bits).
+     *
+     * @return the action to set the IP ToS (DSCP field, 6 bits).
+     */
+    @JsonProperty("actionSetIpToS")
+    public ActionSetIpToS actionSetIpToS() {
+	return actionSetIpToS;
+    }
+
+    /**
+     * Set the action to set the IP ToS (DSCP field, 6 bits).
+     *
+     * @param ipToS the IP ToS (DSCP field, 6 bits) to set.
+     */
+    @JsonProperty("actionSetIpToS")
+    public void setActionSetIpToS(byte ipToS) {
+	actionSetIpToS = new ActionSetIpToS(ipToS);
+	actionType = ActionValues.ACTION_SET_NW_TOS;
+    }
+
+    /**
+     * Get the action to set the TCP/UDP source port.
+     *
+     * @return the action to set the TCP/UDP source port.
+     */
+    @JsonProperty("actionSetTcpUdpSrcPort")
+    public ActionSetTcpUdpPort actionSetTcpUdpSrcPort() {
+	return actionSetTcpUdpSrcPort;
+    }
+
+    /**
+     * Set the action to set the TCP/UDP source port.
+     *
+     * @param port the TCP/UDP port to set as the TCP/UDP source port.
+     */
+    @JsonProperty("actionSetTcpUdpSrcPort")
+    public void setActionSetTcpUdpSrcPort(short port) {
+	actionSetTcpUdpSrcPort = new ActionSetTcpUdpPort(port);
+	actionType = ActionValues.ACTION_SET_TP_SRC;
+    }
+
+    /**
+     * Get the action to set the TCP/UDP destination port.
+     *
+     * @return the action to set the TCP/UDP destination port.
+     */
+    @JsonProperty("actionSetTcpUdpDstPort")
+    public ActionSetTcpUdpPort actionSetTcpUdpDstPort() {
+	return actionSetTcpUdpDstPort;
+    }
+
+    /**
+     * Set the action to set the TCP/UDP destination port.
+     *
+     * @param port the TCP/UDP port to set as the TCP/UDP destination port.
+     */
+    @JsonProperty("actionSetTcpUdpDstPort")
+    public void setActionSetTcpUdpDstPort(short port) {
+	actionSetTcpUdpDstPort = new ActionSetTcpUdpPort(port);
+	actionType = ActionValues.ACTION_SET_TP_DST;
+    }
+
+    /**
+     * Get the action to output to queue on a port.
+     *
+     * @return the action to output to queue on a port.
+     */
+    @JsonProperty("actionEnqueue")
+    public ActionEnqueue actionEnqueue() { return actionEnqueue; }
+
+    /**
+     * Set the action to output to queue on a port.
+     *
+     * @param port the port to set.
+     * @param int queueId the queue ID to set.
+     */
+    @JsonProperty("actionEnqueue")
+    public void setActionEnqueue(Port port, int queueId) {
+	actionEnqueue = new ActionEnqueue(port, queueId);
+	actionType = ActionValues.ACTION_ENQUEUE;
+    }
+
+    /**
+     * Convert the set of actions to a string.
+     *
+     * The string has the following form:
+     *  [type=XXX action=XXX]
+     *
+     * @return the set of actions as a string.
+     */
+    @Override
+    public String toString() {
+	String ret = "[";
+	ret += "type=" + actionType;
+	switch (actionType) {
+	case ACTION_OUTPUT:
+	    ret += " action=" + actionOutput.toString();
+	    break;
+	case ACTION_SET_VLAN_VID:
+	    ret += " action=" + actionSetVlanId.toString();
+	    break;
+	case ACTION_SET_VLAN_PCP:
+	    ret += " action=" + actionSetVlanPriority.toString();
+	    break;
+	case ACTION_STRIP_VLAN:
+	    ret += " action=" + actionStripVlan.toString();
+	    break;
+	case ACTION_SET_DL_SRC:
+	    ret += " action=" + actionSetEthernetSrcAddr.toString();
+	    break;
+	case ACTION_SET_DL_DST:
+	    ret += " action=" + actionSetEthernetDstAddr.toString();
+	    break;
+	case ACTION_SET_NW_SRC:
+	    ret += " action=" + actionSetIPv4SrcAddr.toString();
+	    break;
+	case ACTION_SET_NW_DST:
+	    ret += " action=" + actionSetIPv4DstAddr.toString();
+	    break;
+	case ACTION_SET_NW_TOS:
+	    ret += " action=" + actionSetIpToS.toString();
+	    break;
+	case ACTION_SET_TP_SRC:
+	    ret += " action=" + actionSetTcpUdpSrcPort.toString();
+	    break;
+	case ACTION_SET_TP_DST:
+	    ret += " action=" + actionSetTcpUdpDstPort.toString();
+	    break;
+	case ACTION_ENQUEUE:
+	    ret += " action=" + actionEnqueue.toString();
+	    break;
+	}
+	ret += "]";
+
+	return ret;
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryActions.java b/src/main/java/net/floodlightcontroller/util/FlowEntryActions.java
deleted file mode 100644
index 4d17de8..0000000
--- a/src/main/java/net/floodlightcontroller/util/FlowEntryActions.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package net.floodlightcontroller.util;
-
-import org.codehaus.jackson.annotate.JsonProperty;
-
-/**
- * The class representing the Flow Entry set of actions.
- *
- * The Flow Entry set of actions need to be applied to each packet.
- *
- * NOTE: This is just an empty placeholder (for now). The implied action is
- * forwarding on a single port.
- */
-public class FlowEntryActions {
-
-    /**
-     * Default constructor.
-     */
-    public FlowEntryActions() {
-    }
-
-    /**
-     * Convert the set of actions to a string.
-     *
-     * @return the set of actions as a string.
-     */
-    @Override
-    public String toString() {
-	String ret = "";
-	// TODO: Implement it!
-	return ret;
-    }
-}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryId.java b/src/main/java/net/floodlightcontroller/util/FlowEntryId.java
index 0874bdb..d322f5e 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntryId.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryId.java
@@ -63,6 +63,6 @@
      */
     @Override
     public String toString() {
-	return Long.toHexString(this.value);
+	return "0x" + Long.toHexString(this.value);
     }
 }
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java b/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
index 9bd3bea..64527c5 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryMatch.java
@@ -10,18 +10,76 @@
  *
  * The Flow Entry matching filter that is used to specify
  * the network data that would be forwarded on the data path from
- * the source to the destination. Examples: MAC address (of the
- * sender), IP prefix that includes the destination's IP address, etc.
- *
- * NOTE: The FlowEntryMatch specification below is incomplete: we need
- * more matching fields, we need to indicate which fields need to be
- * matched, etc.
+ * the source to the destination. Examples: source or destination MAC address,
+ * IP prefix that includes the destination's IP address, etc.
  */
 public class FlowEntryMatch {
-    private MACAddress srcMac;		// Matching source MAC address
-    private MACAddress dstMac;		// Matching destination MAC address
-    private IPv4Net srcIPv4Net;		// Matching source IPv4 prefix
-    private IPv4Net dstIPv4Net;		// Matching destination IPv4 prefix
+    /**
+     * A class for storing a value to match.
+     */
+    class Field<T> {
+	/**
+	 * Default constructor.
+	 */
+	public Field() {
+	    this.enabled = false;
+	}
+
+	/**
+	 * Constructor for a given value to match.
+	 */
+	public Field(T value) {
+	    this.value = value;
+	    this.enabled = true;
+	}
+
+	/**
+	 * Get the value.
+	 *
+	 * @return the value.
+	 */
+	public T value() { return this.value; }
+
+	/**
+	 * Enable the matching for a given value.
+	 *
+	 * @param value the value to set.
+	 */
+	public void enableMatch(T value) {
+	    this.value = value;
+	    this.enabled = true;
+	}
+
+	/**
+	 * Disable the matching.
+	 */
+	public void disableMatch() {
+	    this.enabled = false;
+	}
+
+	/**
+	 * Test whether matching is enabled.
+	 *
+	 * @return true if matching is enabled, otherwise false.
+	 */
+	public boolean enabled() { return this.enabled; }
+
+	private T value;		// The value to match
+	private boolean enabled;	// Set to true, if matching is enabled
+    }
+
+    private Field<Port> inPort;		// Matching input switch port
+    private Field<MACAddress> srcMac;	// Matching source MAC address
+    private Field<MACAddress> dstMac;	// Matching destination MAC address
+    private Field<Short> vlanId;	// Matching VLAN ID
+    private Field<Byte> vlanPriority;	// Matching VLAN priority
+    private Field<Short> ethernetFrameType; // Matching Ethernet frame type
+    private Field<Byte> ipToS;		// Matching IP ToS (DSCP field, 6 bits)
+    private Field<Byte> ipProto;	// Matching IP protocol
+    private Field<IPv4Net> srcIPv4Net;	// Matching source IPv4 prefix
+    private Field<IPv4Net> dstIPv4Net;	// Matching destination IPv4 prefix
+    private Field<Short> srcTcpUdpPort;	// Matching source TCP/UDP port
+    private Field<Short> dstTcpUdpPort;	// Matching destination TCP/UDP port
 
     /**
      * Default constructor.
@@ -30,21 +88,85 @@
     }
 
     /**
+     * Get the matching input switch port.
+     *
+     * @return the matching input switch port.
+     */
+    @JsonProperty("inPort")
+    public Port inPort() {
+	if (inPort != null)
+	    return inPort.value();
+	return null;
+    }
+
+    /**
+     * Enable the matching on input switch port.
+     *
+     * @param inPort the input switch port value to enable for matching.
+     */
+    @JsonProperty("inPort")
+    public void enableInPort(Port inPort) {
+	this.inPort = new Field<Port>(inPort);
+    }
+
+    /**
+     * Disable the matching on input switch port.
+     */
+    public void disableInPort() {
+	this.inPort = null;
+    }
+
+    /**
+     * Test if matching on input switch port is enabled.
+     *
+     * @return true if matching on input switch port is enabled.
+     */
+    @JsonProperty("matchInPort")
+    public boolean matchInPort() {
+	if (inPort != null)
+	    return inPort.enabled();
+	return false;
+    }
+
+    /**
      * Get the matching source MAC address.
      *
      * @return the matching source MAC address.
      */
     @JsonProperty("srcMac")
-    public MACAddress srcMac() { return srcMac; }
+    public MACAddress srcMac() {
+	if (srcMac != null)
+	    return srcMac.value();
+	return null;
+    }
 
     /**
-     * Set the matching source MAC address.
+     * Enable the matching on source MAC address.
      *
-     * @param srcMac the matching source MAC address to set.
+     * @param srcMac the source MAC address value to enable for matching.
      */
     @JsonProperty("srcMac")
-    public void setSrcMac(MACAddress srcMac) {
-	this.srcMac = srcMac;
+    public void enableSrcMac(MACAddress srcMac) {
+	this.srcMac = new Field<MACAddress>(srcMac);
+    }
+
+    /**
+     * Disable the matching on source MAC address.
+     */
+    public void disableSrcMac() {
+	this.srcMac = null;
+    }
+
+    /**
+     * Test if matching on source MAC address is enabled.
+     *
+     * @return true if matching on source MAC address is enabled.
+     */
+    @JsonProperty("matchSrcMac")
+    public boolean matchSrcMac() {
+	if (srcMac != null)
+	    return srcMac.enabled();
+	return false;
     }
 
     /**
@@ -53,16 +175,245 @@
      * @return the matching destination MAC address.
      */
     @JsonProperty("dstMac")
-    public MACAddress dstMac() { return dstMac; }
+    public MACAddress dstMac() {
+	if (dstMac != null)
+	    return dstMac.value();
+	return null;
+    }
 
     /**
-     * Set the matching destination MAC address.
+     * Enable the matching on destination MAC address.
      *
-     * @param dstMac the matching destination MAC address to set.
+     * @param dstMac the destination MAC address value to enable for matching.
      */
     @JsonProperty("dstMac")
-    public void setDstMac(MACAddress dstMac) {
-	this.dstMac = dstMac;
+    public void enableDstMac(MACAddress dstMac) {
+	this.dstMac = new Field<MACAddress>(dstMac);
+    }
+
+    /**
+     * Disable the matching on destination MAC address.
+     */
+    public void disableDstMac() {
+	this.dstMac = null;
+    }
+
+    /**
+     * Test if matching on destination MAC address is enabled.
+     *
+     * @return true if matching on destination MAC address is enabled.
+     */
+    @JsonProperty("matchDstMac")
+    public boolean matchDstMac() {
+	if (dstMac != null)
+	    return dstMac.enabled();
+	return false;
+    }
+
+    /**
+     * Get the matching VLAN ID.
+     *
+     * @return the matching VLAN ID.
+     */
+    @JsonProperty("vlanId")
+    public Short vlanId() {
+	if (vlanId != null)
+	    return vlanId.value();
+	return null;
+    }
+
+    /**
+     * Enable the matching on VLAN ID.
+     *
+     * @param vlanId the VLAN ID value to enable for matching.
+     */
+    @JsonProperty("vlanId")
+    public void enableVlanId(Short vlanId) {
+	this.vlanId = new Field<Short>(vlanId);
+    }
+
+    /**
+     * Disable the matching on VLAN ID.
+     */
+    public void disableVlanId() {
+	this.vlanId = null;
+    }
+
+    /**
+     * Test if matching on VLAN ID is enabled.
+     *
+     * @return true if matching on VLAN ID is enabled.
+     */
+    @JsonProperty("matchVlanId")
+    public boolean matchVlanId() {
+	if (vlanId != null)
+	    return vlanId.enabled();
+	return false;
+    }
+
+    /**
+     * Get the matching VLAN priority.
+     *
+     * @return the matching VLAN priority.
+     */
+    @JsonProperty("vlanPriority")
+    public Byte vlanPriority() {
+	if (vlanPriority != null)
+	    return vlanPriority.value();
+	return null;
+    }
+
+    /**
+     * Enable the matching on VLAN priority.
+     *
+     * @param vlanPriority the VLAN priority value to enable for matching.
+     */
+    @JsonProperty("vlanPriority")
+    public void enableVlanPriority(Byte vlanPriority) {
+	this.vlanPriority = new Field<Byte>(vlanPriority);
+    }
+
+    /**
+     * Disable the matching on VLAN priority.
+     */
+    public void disableVlanPriority() {
+	this.vlanPriority = null;
+    }
+
+    /**
+     * Test if matching on VLAN priority is enabled.
+     *
+     * @return true if matching on VLAN priority is enabled.
+     */
+    @JsonProperty("matchVlanPriority")
+    public boolean matchVlanPriority() {
+	if (vlanPriority != null)
+	    return vlanPriority.enabled();
+	return false;
+    }
+
+    /**
+     * Get the matching Ethernet frame type.
+     *
+     * @return the matching Ethernet frame type.
+     */
+    @JsonProperty("ethernetFrameType")
+    public Short ethernetFrameType() {
+	if (ethernetFrameType != null)
+	    return ethernetFrameType.value();
+	return null;
+    }
+
+    /**
+     * Enable the matching on Ethernet frame type.
+     *
+     * @param ethernetFrameType the Ethernet frame type value to enable for
+     * matching.
+     */
+    @JsonProperty("ethernetFrameType")
+    public void enableEthernetFrameType(Short ethernetFrameType) {
+	this.ethernetFrameType = new Field<Short>(ethernetFrameType);
+    }
+
+    /**
+     * Disable the matching on Ethernet frame type.
+     */
+    public void disableEthernetFrameType() {
+	this.ethernetFrameType = null;
+    }
+
+    /**
+     * Test if matching on Ethernet frame type is enabled.
+     *
+     * @return true if matching on Ethernet frame type is enabled.
+     */
+    @JsonProperty("matchEthernetFrameType")
+    public boolean matchEthernetFrameType() {
+	if (ethernetFrameType != null)
+	    return ethernetFrameType.enabled();
+	return false;
+    }
+
+    /**
+     * Get the matching IP ToS (DSCP field, 6 bits)
+     *
+     * @return the matching IP ToS.
+     */
+    @JsonProperty("ipToS")
+    public Byte ipToS() {
+	if (ipToS != null)
+	    return ipToS.value();
+	return null;
+    }
+
+    /**
+     * Enable the matching on IP ToS (DSCP field, 6 bits).
+     *
+     * @param ipToS the IP ToS value to enable for matching.
+     */
+    @JsonProperty("ipToS")
+    public void enableIpToS(Byte ipToS) {
+	this.ipToS = new Field<Byte>(ipToS);
+    }
+
+    /**
+     * Disable the matching on IP ToS (DSCP field, 6 bits).
+     */
+    public void disableIpToS() {
+	this.ipToS = null;
+    }
+
+    /**
+     * Test if matching on IP ToS (DSCP field, 6 bits) is enabled.
+     *
+     * @return true if matching on IP ToS is enabled.
+     */
+    @JsonProperty("matchIpToS")
+    public boolean matchIpToS() {
+	if (ipToS != null)
+	    return ipToS.enabled();
+	return false;
+    }
+
+    /**
+     * Get the matching IP protocol.
+     *
+     * @return the matching IP protocol.
+     */
+    @JsonProperty("ipProto")
+    public Byte ipProto() {
+	if (ipProto != null)
+	    return ipProto.value();
+	return null;
+    }
+
+    /**
+     * Enable the matching on IP protocol.
+     *
+     * @param ipProto the IP protocol value to enable for matching.
+     */
+    @JsonProperty("ipProto")
+    public void enableIpProto(Byte ipProto) {
+	this.ipProto = new Field<Byte>(ipProto);
+    }
+
+    /**
+     * Disable the matching on IP protocol.
+     */
+    public void disableIpProto() {
+	this.ipProto = null;
+    }
+
+    /**
+     * Test if matching on IP protocol is enabled.
+     *
+     * @return true if matching on IP protocol is enabled.
+     */
+    @JsonProperty("matchIpProto")
+    public boolean matchIpProto() {
+	if (ipProto != null)
+	    return ipProto.enabled();
+	return false;
     }
 
     /**
@@ -71,16 +422,39 @@
      * @return the matching source IPv4 prefix.
      */
     @JsonProperty("srcIPv4Net")
-    public IPv4Net srcIPv4Net() { return srcIPv4Net; }
+    public IPv4Net srcIPv4Net() {
+	if (srcIPv4Net != null)
+	    return srcIPv4Net.value();
+	return null;
+    }
 
     /**
-     * Set the matching source IPv4 prefix.
+     * Enable the matching on source IPv4 prefix.
      *
-     * @param srcIPv4Net the matching source IPv4 prefix to set.
+     * @param srcIPv4Net the source IPv4 prefix value to enable for matching.
      */
     @JsonProperty("srcIPv4Net")
-    public void setSrcIPv4Net(IPv4Net srcIPv4Net) {
-	this.srcIPv4Net = srcIPv4Net;
+    public void enableSrcIPv4Net(IPv4Net srcIPv4Net) {
+	this.srcIPv4Net = new Field<IPv4Net>(srcIPv4Net);
+    }
+
+    /**
+     * Disable the matching on source IPv4 prefix.
+     */
+    public void disableSrcIPv4Net() {
+	this.srcIPv4Net = null;
+    }
+
+    /**
+     * Test if matching on source IPv4 prefix is enabled.
+     *
+     * @return true if matching on source IPv4 prefix is enabled.
+     */
+    @JsonProperty("matchSrcIPv4Net")
+    public boolean matchSrcIPv4Net() {
+	if (srcIPv4Net != null)
+	    return srcIPv4Net.enabled();
+	return false;
     }
 
     /**
@@ -89,16 +463,123 @@
      * @return the matching destination IPv4 prefix.
      */
     @JsonProperty("dstIPv4Net")
-    public IPv4Net dstIPv4Net() { return dstIPv4Net; }
+    public IPv4Net dstIPv4Net() {
+	if (dstIPv4Net != null)
+	    return dstIPv4Net.value();
+	return null;
+    }
 
     /**
-     * Set the matching destination IPv4 prefix.
+     * Enable the matching on destination IPv4 prefix.
      *
-     * @param srcIPv4Net the matching destination IPv4 prefix to set.
+     * @param dstIPv4Net the destination IPv4 prefix value to enable for
+     * matching.
      */
     @JsonProperty("dstIPv4Net")
-    public void setDstIPv4Net(IPv4Net dstIPv4Net) {
-	this.dstIPv4Net = dstIPv4Net;
+    public void enableDstIPv4Net(IPv4Net dstIPv4Net) {
+	this.dstIPv4Net = new Field<IPv4Net>(dstIPv4Net);
+    }
+
+    /**
+     * Disable the matching on destination IPv4 prefix.
+     */
+    public void disableDstIPv4Net() {
+	this.dstIPv4Net = null;
+    }
+
+    /**
+     * Test if matching on destination IPv4 prefix is enabled.
+     *
+     * @return true if matching on destination IPv4 prefix is enabled.
+     */
+    @JsonProperty("matchDstIPv4Net")
+    public boolean matchDstIPv4Net() {
+	if (dstIPv4Net != null)
+	    return dstIPv4Net.enabled();
+	return false;
+    }
+
+    /**
+     * Get the matching source TCP/UDP port.
+     *
+     * @return the matching source TCP/UDP port.
+     */
+    @JsonProperty("srcTcpUdpPort")
+    public Short srcTcpUdpPort() {
+	if (srcTcpUdpPort != null)
+	    return srcTcpUdpPort.value();
+	return null;
+    }
+
+    /**
+     * Enable the matching on source TCP/UDP port.
+     *
+     * @param srcTcpUdpPort the source TCP/UDP port to enable for matching.
+     */
+    @JsonProperty("srcTcpUdpPort")
+    public void enableSrcTcpUdpPort(Short srcTcpUdpPort) {
+	this.srcTcpUdpPort = new Field<Short>(srcTcpUdpPort);
+    }
+
+    /**
+     * Disable the matching on source TCP/UDP port.
+     */
+    public void disableSrcTcpUdpPort() {
+	this.srcTcpUdpPort = null;
+    }
+
+    /**
+     * Test if matching on source TCP/UDP port is enabled.
+     *
+     * @return true if matching on source TCP/UDP port is enabled.
+     */
+    @JsonProperty("matchSrcTcpUdpPort")
+    public boolean matchSrcTcpUdpPort() {
+	if (srcTcpUdpPort != null)
+	    return srcTcpUdpPort.enabled();
+	return false;
+    }
+
+    /**
+     * Get the matching destination TCP/UDP port.
+     *
+     * @return the matching destination TCP/UDP port.
+     */
+    @JsonProperty("dstTcpUdpPort")
+    public Short dstTcpUdpPort() {
+	if (dstTcpUdpPort != null)
+	    return dstTcpUdpPort.value();
+	return null;
+    }
+
+    /**
+     * Enable the matching on destination TCP/UDP port.
+     *
+     * @param dstTcpUdpPort the destination TCP/UDP port to enable for
+     * matching.
+     */
+    @JsonProperty("dstTcpUdpPort")
+    public void enableDstTcpUdpPort(Short dstTcpUdpPort) {
+	this.dstTcpUdpPort = new Field<Short>(dstTcpUdpPort);
+    }
+
+    /**
+     * Disable the matching on destination TCP/UDP port.
+     */
+    public void disableDstTcpUdpPort() {
+	this.dstTcpUdpPort = null;
+    }
+
+    /**
+     * Test if matching on destination TCP/UDP port is enabled.
+     *
+     * @return true if matching on destination TCP/UDP port is enabled.
+     */
+    @JsonProperty("matchDstTcpUdpPort")
+    public boolean matchDstTcpUdpPort() {
+	if (dstTcpUdpPort != null)
+	    return dstTcpUdpPort.enabled();
+	return false;
     }
 
     /**
@@ -111,11 +592,87 @@
      */
     @Override
     public String toString() {
-	String ret = "[srcMac=" + this.srcMac.toString();
-	ret += " dstMac=" + this.dstMac.toString();
-	ret += " srcIPv4Net=" + this.srcIPv4Net.toString();
-	ret += " dstIPv4Net=" + this.dstIPv4Net.toString();
+	String ret = "[";
+	boolean addSpace = false;
+
+	//
+	// Conditionally add only those matching fields that are enabled
+	//
+	if (matchInPort()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "inPort=" + this.inPort().toString();
+	}
+	if (matchSrcMac()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "srcMac=" + this.srcMac().toString();
+	}
+	if (matchDstMac()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "dstMac=" + this.dstMac().toString();
+	}
+	if (matchVlanId()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "vlanId=" + this.vlanId().toString();
+	}
+	if (matchVlanPriority()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "vlanPriority=" + this.vlanPriority().toString();
+	}
+	if (matchEthernetFrameType()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "ethernetFrameType=" + this.ethernetFrameType().toString();
+	}
+	if (matchIpToS()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "ipToS=" + this.ipToS().toString();
+	}
+	if (matchIpProto()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "ipProto=" + this.ipProto().toString();
+	}
+	if (matchSrcIPv4Net()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "srcIPv4Net=" + this.srcIPv4Net().toString();
+	}
+	if (matchDstIPv4Net()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "dstIPv4Net=" + this.dstIPv4Net().toString();
+	}
+	if (matchSrcTcpUdpPort()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "srcTcpUdpPort=" + this.srcTcpUdpPort().toString();
+	}
+	if (matchDstTcpUdpPort()) {
+	    if (addSpace)
+		ret += " ";
+	    addSpace = true;
+	    ret += "dstTcpUdpPort=" + this.dstTcpUdpPort().toString();
+	}
+
 	ret += "]";
+
 	return ret;
     }
 }
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntrySwitchState.java b/src/main/java/net/floodlightcontroller/util/FlowEntrySwitchState.java
new file mode 100644
index 0000000..4f9882a
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntrySwitchState.java
@@ -0,0 +1,12 @@
+package net.floodlightcontroller.util;
+
+/**
+ * The Flow Entry state as set by the controller.
+ */
+public enum FlowEntrySwitchState {
+	FE_SWITCH_UNKNOWN,		// Initialization value: state unknown
+	FE_SWITCH_NOT_UPDATED,		// Switch not updated with this entry
+	FE_SWITCH_UPDATE_IN_PROGRESS,	// Switch update in progress
+	FE_SWITCH_UPDATED,		// Switch updated with this entry
+	FE_SWITCH_UPDATE_FAILED	// Error updating the switch with this entry
+}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowEntryUserState.java b/src/main/java/net/floodlightcontroller/util/FlowEntryUserState.java
new file mode 100644
index 0000000..8637b4f
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/FlowEntryUserState.java
@@ -0,0 +1,11 @@
+package net.floodlightcontroller.util;
+
+/**
+ * The Flow Entry state as set by the user (via the ONOS API).
+ */
+public enum FlowEntryUserState {
+	FE_USER_UNKNOWN,		// Initialization value: state unknown
+	FE_USER_ADD,			// Flow entry that is added
+	FE_USER_MODIFY,			// Flow entry that is modified
+	FE_USER_DELETE			// Flow entry that is deleted
+}
diff --git a/src/main/java/net/floodlightcontroller/util/FlowPath.java b/src/main/java/net/floodlightcontroller/util/FlowPath.java
index 5b3bbd1..11f23fe 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowPath.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowPath.java
@@ -18,6 +18,7 @@
      * Default constructor.
      */
     public FlowPath() {
+	dataPath = new DataPath();
     }
 
     /**
diff --git a/src/main/java/net/floodlightcontroller/util/MACAddress.java b/src/main/java/net/floodlightcontroller/util/MACAddress.java
index 4ba9dad..743dc5b 100644
--- a/src/main/java/net/floodlightcontroller/util/MACAddress.java
+++ b/src/main/java/net/floodlightcontroller/util/MACAddress.java
@@ -2,11 +2,20 @@
 
 import java.util.Arrays;
 
+import net.floodlightcontroller.util.serializers.MACAddressDeserializer;
+import net.floodlightcontroller.util.serializers.MACAddressSerializer;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
 /**
  * The class representing MAC address.
  *
  * @author Sho Shimizu (sho.shimizu@gmail.com)
  */
+@JsonDeserialize(using=MACAddressDeserializer.class)
+@JsonSerialize(using=MACAddressSerializer.class)
 public class MACAddress {
     public static final int MAC_ADDRESS_LENGTH = 6;
     private byte[] address = new byte[MAC_ADDRESS_LENGTH];
diff --git a/src/main/java/net/floodlightcontroller/util/Port.java b/src/main/java/net/floodlightcontroller/util/Port.java
index 19bbf8f..41f0d55 100644
--- a/src/main/java/net/floodlightcontroller/util/Port.java
+++ b/src/main/java/net/floodlightcontroller/util/Port.java
@@ -5,7 +5,69 @@
 /**
  * The class representing a network port of a switch.
  */
+
 public class Port {
+    /**
+     * Special port values.
+     *
+     * Those values are taken as-is from the OpenFlow-v1.0.0 specification
+     * (pp 18-19).
+     */
+    public enum PortValues {
+	/* Maximum number of physical switch ports. */
+	PORT_MAX		((short)0xff00),
+
+	/* Fake output "ports". */
+
+	/* Send the packet out the input port. This
+	   virtual port must be explicitly used
+	   in order to send back out of the input
+	   port. */
+	PORT_IN_PORT		((short)0xfff8),
+
+	/* Perform actions in flow table.
+	   NB: This can only be the destination
+	   port for packet-out messages. */
+	PORT_TABLE		((short)0xfff9),
+
+	/* Process with normal L2/L3 switching. */
+	PORT_NORMAL		((short)0xfffa),
+
+	/* All physical ports except input port and
+	   those disabled by STP. */
+	PORT_FLOOD		((short)0xfffb),
+
+	/* All physical ports except input port. */
+	PORT_ALL		((short)0xfffc),
+
+	/* Send to controller. */
+	PORT_CONTROLLER		((short)0xfffd),
+
+	/* Local openflow "port". */
+	PORT_LOCAL		((short)0xfffe),
+
+	/* Not associated with a physical port. */
+	PORT_NONE		((short)0xffff);
+
+	private final short value;	// The value
+
+	/**
+	 * Constructor for a given value.
+	 *
+	 * @param value the value to use for the initialization.
+	 */
+	private PortValues(short value) {
+	    this.value = value;
+	}
+
+	/**
+	 * Get the value as a short integer.
+	 *
+	 * @return the value as a short integer.
+	 */
+	private short value() { return this.value; }
+    }
+
     private short value;
 
     /**
@@ -25,7 +87,7 @@
     }
 
     /**
-     * Constructor from a long value.
+     * Constructor from a short integer value.
      *
      * @param value the value to use.
      */
@@ -34,6 +96,15 @@
     }
 
     /**
+     * Constructor from a PortValues enum value.
+     *
+     * @param value the value to use.
+     */
+    public Port(PortValues value) {
+	this.value = value.value();
+    }
+
+    /**
      * Get the value of the port.
      *
      * @return the value of the port.
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/IPv4Deserializer.java b/src/main/java/net/floodlightcontroller/util/serializers/IPv4Deserializer.java
index 7ce7d5c..275f9f0 100644
--- a/src/main/java/net/floodlightcontroller/util/serializers/IPv4Deserializer.java
+++ b/src/main/java/net/floodlightcontroller/util/serializers/IPv4Deserializer.java
@@ -16,7 +16,7 @@
 import org.slf4j.LoggerFactory;
 
 /**
- * Deserialize an IPv4 from a string.
+ * Deserialize an IPv4 address from a string.
  */
 public class IPv4Deserializer extends JsonDeserializer<IPv4> {
 
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/IPv4NetDeserializer.java b/src/main/java/net/floodlightcontroller/util/serializers/IPv4NetDeserializer.java
index e35fc80..3c36870 100644
--- a/src/main/java/net/floodlightcontroller/util/serializers/IPv4NetDeserializer.java
+++ b/src/main/java/net/floodlightcontroller/util/serializers/IPv4NetDeserializer.java
@@ -16,7 +16,7 @@
 import org.slf4j.LoggerFactory;
 
 /**
- * Deserialize an IPv4Net from a string.
+ * Deserialize an IPv4Net address from a string.
  */
 public class IPv4NetDeserializer extends JsonDeserializer<IPv4Net> {
 
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/IPv6Deserializer.java b/src/main/java/net/floodlightcontroller/util/serializers/IPv6Deserializer.java
index 6713f93..818de30 100644
--- a/src/main/java/net/floodlightcontroller/util/serializers/IPv6Deserializer.java
+++ b/src/main/java/net/floodlightcontroller/util/serializers/IPv6Deserializer.java
@@ -16,7 +16,7 @@
 import org.slf4j.LoggerFactory;
 
 /**
- * Deserialize an IPv6 from a string.
+ * Deserialize an IPv6 address from a string.
  */
 public class IPv6Deserializer extends JsonDeserializer<IPv6> {
 
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/IPv6NetDeserializer.java b/src/main/java/net/floodlightcontroller/util/serializers/IPv6NetDeserializer.java
index 596ee50..375dc26 100644
--- a/src/main/java/net/floodlightcontroller/util/serializers/IPv6NetDeserializer.java
+++ b/src/main/java/net/floodlightcontroller/util/serializers/IPv6NetDeserializer.java
@@ -16,7 +16,7 @@
 import org.slf4j.LoggerFactory;
 
 /**
- * Deserialize an IPv6Net from a string.
+ * Deserialize an IPv6Net address from a string.
  */
 public class IPv6NetDeserializer extends JsonDeserializer<IPv6Net> {
 
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/MACAddressDeserializer.java b/src/main/java/net/floodlightcontroller/util/serializers/MACAddressDeserializer.java
new file mode 100644
index 0000000..35b384d
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/serializers/MACAddressDeserializer.java
@@ -0,0 +1,43 @@
+package net.floodlightcontroller.util.serializers;
+
+import java.io.IOException;
+
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.ObjectCodec;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.codehaus.jackson.map.DeserializationContext;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Deserialize a MAC address from a string.
+ */
+public class MACAddressDeserializer extends JsonDeserializer<MACAddress> {
+
+    protected static Logger log = LoggerFactory.getLogger(MACAddressDeserializer.class);
+
+    @Override
+    public MACAddress deserialize(JsonParser jp,
+				  DeserializationContext ctxt)
+	throws IOException, JsonProcessingException {
+
+	MACAddress mac = null;
+
+	jp.nextToken();		// Move to JsonToken.START_OBJECT
+	while (jp.nextToken() != JsonToken.END_OBJECT) {
+	    String fieldname = jp.getCurrentName();
+	    if ("value".equals(fieldname)) {
+		String value = jp.getText();
+		log.debug("Fieldname: " + fieldname + " Value: " + value);
+		mac = MACAddress.valueOf(value);
+	    }
+	}
+	return mac;
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/util/serializers/MACAddressSerializer.java b/src/main/java/net/floodlightcontroller/util/serializers/MACAddressSerializer.java
new file mode 100644
index 0000000..dec2596
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/serializers/MACAddressSerializer.java
@@ -0,0 +1,25 @@
+package net.floodlightcontroller.util.serializers;
+
+import java.io.IOException;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+import net.floodlightcontroller.util.MACAddress;
+
+/**
+ * Serialize a MAC address as a string.
+ */
+public class MACAddressSerializer extends JsonSerializer<MACAddress> {
+
+    @Override
+    public void serialize(MACAddress mac, JsonGenerator jGen,
+			  SerializerProvider serializer)
+	throws IOException, JsonProcessingException {
+	jGen.writeStartObject();
+	jGen.writeStringField("value", mac.toString());
+	jGen.writeEndObject();
+    }
+}
diff --git a/src/main/java/net/onrc/onos/util/GraphDBConnection.java b/src/main/java/net/onrc/onos/util/GraphDBConnection.java
index a2a8689..724095b 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBConnection.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBConnection.java
@@ -37,6 +37,13 @@
 		        if (!s.contains("dl_address")) {
 		        	graph.createKeyIndex("dl_address", Vertex.class);
 		        }
+		        if (!s.contains("flow_id")) {
+		        	graph.createKeyIndex("flow_id", Vertex.class);
+		        }
+		        if (!s.contains("flow_entry_id")) {
+		        	graph.createKeyIndex("flow_entry_id",
+						     Vertex.class);
+		        }
 		   }
 		   graph.stopTransaction(Conclusion.SUCCESS);
 		   if (utils == null) {
diff --git a/src/main/java/net/onrc/onos/util/GraphDBUtils.java b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
index d01de21..097cfa0 100644
--- a/src/main/java/net/onrc/onos/util/GraphDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/GraphDBUtils.java
@@ -7,8 +7,12 @@
 import com.tinkerpop.gremlin.java.GremlinPipeline;
 
 import net.floodlightcontroller.core.INetMapTopologyObjects.IDeviceObject;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
 import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
 import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
+import net.floodlightcontroller.util.FlowEntryId;
+import net.floodlightcontroller.util.FlowId;
 
 public class GraphDBUtils implements IDBUtils {
 
@@ -60,4 +64,76 @@
 		return fg.getVertices("type","device",IDeviceObject.class);
 	}
 
+	@Override
+	public IFlowPath searchFlowPath(GraphDBConnection conn,
+					FlowId flowId) {
+		FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+		
+		return fg.getVertices("flow_id", flowId.toString()).iterator().hasNext() ? 
+		    fg.getVertices("flow_id", flowId.toString(),
+				   IFlowPath.class).iterator().next() : null;
+	}
+
+	@Override
+	public IFlowPath newFlowPath(GraphDBConnection conn) {
+		FramedGraph<TitanGraph> fg = conn.getFramedGraph();	
+		IFlowPath flowPath = fg.addVertex(null, IFlowPath.class);
+		return flowPath;
+	}
+
+	@Override
+	public void removeFlowPath(GraphDBConnection conn,
+				   IFlowPath flowPath) {
+		FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+		fg.removeVertex(flowPath.asVertex());
+	}
+
+	@Override
+	public IFlowPath getFlowPathByFlowEntry(GraphDBConnection conn,
+						IFlowEntry flowEntry) {
+		FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+		GremlinPipeline<Vertex, IFlowPath> pipe = new GremlinPipeline<Vertex, IFlowPath>();
+		pipe.start(flowEntry.asVertex());
+		pipe.out("flow");
+		FramedVertexIterable<IFlowPath> r = new FramedVertexIterable(conn.getFramedGraph(), pipe, IFlowPath.class);
+		return r.iterator().hasNext() ? r.iterator().next() : null;
+	}
+
+	@Override
+        public Iterable<IFlowPath> getAllFlowPaths(GraphDBConnection conn) {
+		FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+		
+		return fg.getVertices("type", "flow", IFlowPath.class);
+	}
+
+	@Override
+	public IFlowEntry searchFlowEntry(GraphDBConnection conn,
+					  FlowEntryId flowEntryId) {
+		FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+		
+		return fg.getVertices("flow_entry_id", flowEntryId.toString()).iterator().hasNext() ? 
+		    fg.getVertices("flow_entry_id", flowEntryId.toString(),
+				   IFlowEntry.class).iterator().next() : null;
+	}
+
+	@Override
+	public IFlowEntry newFlowEntry(GraphDBConnection conn) {
+		FramedGraph<TitanGraph> fg = conn.getFramedGraph();	
+		IFlowEntry flowEntry = fg.addVertex(null, IFlowEntry.class);
+		return flowEntry;
+	}
+
+	@Override
+	public void removeFlowEntry(GraphDBConnection conn,
+				    IFlowEntry flowEntry) {
+		FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+		fg.removeVertex(flowEntry.asVertex());
+	}
+
+	@Override
+        public Iterable<IFlowEntry> getAllFlowEntries(GraphDBConnection conn) {
+		FramedGraph<TitanGraph> fg = conn.getFramedGraph();
+		
+		return fg.getVertices("type", "flow_entry", IFlowEntry.class);
+	}
 }
diff --git a/src/main/java/net/onrc/onos/util/IDBUtils.java b/src/main/java/net/onrc/onos/util/IDBUtils.java
index a27a261..48d5946 100644
--- a/src/main/java/net/onrc/onos/util/IDBUtils.java
+++ b/src/main/java/net/onrc/onos/util/IDBUtils.java
@@ -1,8 +1,12 @@
 package net.onrc.onos.util;
 
 import net.floodlightcontroller.core.INetMapTopologyObjects.IDeviceObject;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowEntry;
+import net.floodlightcontroller.core.INetMapTopologyObjects.IFlowPath;
 import net.floodlightcontroller.core.INetMapTopologyObjects.IPortObject;
 import net.floodlightcontroller.core.INetMapTopologyObjects.ISwitchObject;
+import net.floodlightcontroller.util.FlowEntryId;
+import net.floodlightcontroller.util.FlowId;
 
 public interface IDBUtils {	
 	public ISwitchObject searchSwitch(GraphDBConnection conn, String dpid);
@@ -11,4 +15,16 @@
 	public void removeDevice(GraphDBConnection conn, IDeviceObject dev);
 	public IPortObject searchPort(GraphDBConnection conn, String dpid, short number);
 	public Iterable<IDeviceObject> getDevices(GraphDBConnection conn);
+	public IFlowPath searchFlowPath(GraphDBConnection conn, FlowId flowId);
+	public IFlowPath newFlowPath(GraphDBConnection conn);
+	public void removeFlowPath(GraphDBConnection conn, IFlowPath flowPath);
+        public IFlowPath getFlowPathByFlowEntry(GraphDBConnection conn,
+						IFlowEntry flowEntry);
+	public Iterable<IFlowPath> getAllFlowPaths(GraphDBConnection conn);
+	public IFlowEntry searchFlowEntry(GraphDBConnection conn,
+					  FlowEntryId flowEntryId);
+	public IFlowEntry newFlowEntry(GraphDBConnection conn);
+	public void removeFlowEntry(GraphDBConnection conn,
+				    IFlowEntry flowEntry);
+	public Iterable<IFlowEntry> getAllFlowEntries(GraphDBConnection conn);
 }
diff --git a/src/main/resources/cassandra.titan b/src/main/resources/cassandra.titan
index 8846963..ef6f3ae 100644
--- a/src/main/resources/cassandra.titan
+++ b/src/main/resources/cassandra.titan
@@ -1,3 +1,3 @@
 storage.backend=cassandra
-storage.hostname=onos8vpc
+storage.hostname=localhost
 storage.keyspace=onos
diff --git a/web/add_flow.py b/web/add_flow.py
new file mode 100755
index 0000000..18846b7
--- /dev/null
+++ b/web/add_flow.py
@@ -0,0 +1,121 @@
+#! /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
+
+#
+# curl http://127.0.0.1:8080/wm/topology/route/00:00:00:00:00:00:0a:01/1/00:00:00:00:00:00:0a:04/1/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/topology/route/<srcdpid>/<srcport>/<destdpid>/<destport>/json")
+#
+# Sample output:
+# {'dstPort': {'port': {'value': 0}, 'dpid': {'value': '00:00:00:00:00:00:00:02'}}, 'srcPort': {'port': {'value': 0}, 'dpid': {'value': '00:00:00:00:00:00:00:01'}}, 'flowEntries': [{'outPort': {'value': 1}, 'flowEntryErrorState': None, 'flowEntryMatch': None, 'flowEntryActions': None, 'inPort': {'value': 0}, 'flowEntryId': None, 'flowEntryUserState': 'FE_USER_UNKNOWN', 'dpid': {'value': '00:00:00:00:00:00:00:01'}, 'flowEntrySwitchState': 'FE_SWITCH_UNKNOWN'}, {'outPort': {'value': 0}, 'flowEntryErrorState': None, 'flowEntryMatch': None, 'flowEntryActions': None, 'inPort': {'value': 9}, 'flowEntryId': None, 'flowEntryUserState': 'FE_USER_UNKNOWN', 'dpid': {'value': '00:00:00:00:00:00:00:02'}, 'flowEntrySwitchState': 'FE_SWITCH_UNKNOWN'}]}
+#
+def shortest_path(v1, p1, v2, p2):
+  try:
+    command = "curl -s http://%s:%s/wm/topology/route/%s/%s/%s/%s/json" % (ControllerIP, ControllerPort, v1, p1, v2, p2)
+    debug("shortest_path %s" % command)
+
+    result = os.popen(command).read()
+    debug("result %s" % result)
+    if len(result) == 0:
+	log_error("No Path found")
+	exit(1);
+
+    parsedResult = json.loads(result)
+    debug("parsed %s" % parsedResult)
+
+  except:
+    log_error("Controller IF has issue")
+    exit(1)
+
+  srcSwitch = parsedResult['srcPort']['dpid']['value'];
+  srcPort = parsedResult['srcPort']['port']['value'];
+  dstSwitch = parsedResult['dstPort']['dpid']['value'];
+  dstPort = parsedResult['dstPort']['port']['value'];
+
+  print "DataPath: (src = %s/%s dst = %s/%s)" % (srcSwitch, srcPort, dstSwitch, dstPort);
+
+  for f in parsedResult['flowEntries']:
+    inPort = f['inPort']['value'];
+    outPort = f['outPort']['value'];
+    dpid = f['dpid']['value']
+    print "FlowEntry: (%s, %s, %s)" % (inPort, dpid, outPort)
+
+  return parsedResult
+
+def add_flow_path(flow_path):
+  try:
+    command = "curl -s -H 'Content-Type: application/json' -d '%s' http://%s:%s/wm/flow/add/json" % (flow_path, ControllerIP, ControllerPort)
+    debug("add_flow_path %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)
+
+if __name__ == "__main__":
+  usage_msg = "Usage: %s <flow-id> <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port>" % (sys.argv[0])
+
+  # 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) < 7:
+    log_error(usage_msg)
+    exit(1)
+
+  # Do the work
+  my_flow_id = sys.argv[1]
+  my_installer_id = sys.argv[2];	# 'ONOS-Path-Computation-Python'
+  data_path = shortest_path(sys.argv[3], sys.argv[4], sys.argv[5], sys.argv[6])
+
+  debug("Data Path: %s" % 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
+  flow_path['dataPath'] = data_path
+
+  flow_path_json = json.dumps(flow_path)
+  debug("Flow Path: %s" % flow_path_json)
+
+  add_flow_path(flow_path_json)
diff --git a/web/delete_flow.py b/web/delete_flow.py
new file mode 100755
index 0000000..f6f3d39
--- /dev/null
+++ b/web/delete_flow.py
@@ -0,0 +1,62 @@
+#! /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/delete/<flow-id>/json")
+def delete_flow_path(flow_id):
+  command = "curl -s \"http://%s:%s/wm/flow/delete/%s/json\"" % (ControllerIP, ControllerPort, flow_id)
+  debug("delete_flow_path %s" % command)
+  result = os.popen(command).read()
+  debug("result %s" % result)
+  # parsedResult = json.loads(result)
+  # debug("parsed %s" % parsedResult)
+
+if __name__ == "__main__":
+  usage_msg = "Usage: %s <flow_id>" % (sys.argv[0])
+
+  # 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)
+
+  # Do the work
+  delete_flow_path(sys.argv[1]);
diff --git a/web/get_flow.py b/web/get_flow.py
new file mode 100755
index 0000000..dd6a8b6
--- /dev/null
+++ b/web/get_flow.py
@@ -0,0 +1,264 @@
+#! /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
+
+## 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/get/<flow-id>/json")
+# 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 print_flow_path(parsedResult):
+  flowId = parsedResult['flowId']['value']
+  installerId = parsedResult['installerId']['value']
+  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)
+
+  for f in parsedResult['dataPath']['flowEntries']:
+    inPort = f['inPort']['value']
+    outPort = f['outPort']['value']
+    dpid = f['dpid']['value']
+    userState = f['flowEntryUserState']
+    switchState = f['flowEntrySwitchState']
+    match = f['flowEntryMatch'];
+    actions = f['flowEntryActions']
+    print "  FlowEntry: (%s, %s, %s, %s, %s)" % (inPort, dpid, outPort, userState, switchState)
+
+    inPort = match['inPort']
+    matchInPort = match['matchInPort']
+    srcMac = match['srcMac']
+    matchSrcMac = match['matchSrcMac']
+    dstMac = match['dstMac']
+    matchDstMac = match['matchDstMac']
+    vlanId = match['vlanId']
+    matchVlanId = match['matchVlanId']
+    vlanPriority = match['vlanPriority']
+    matchVlanPriority = match['matchVlanPriority']
+    ethernetFrameType = match['ethernetFrameType']
+    matchEthernetFrameType = match['matchEthernetFrameType']
+    ipToS = match['ipToS']
+    matchIpToS = match['matchIpToS']
+    ipProto = match['ipProto']
+    matchIpProto = match['matchIpProto']
+    srcIPv4Net = match['srcIPv4Net']
+    matchSrcIPv4Net = match['matchSrcIPv4Net']
+    dstIPv4Net = match['dstIPv4Net']
+    matchDstIPv4Net = match['matchDstIPv4Net']
+    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 matchVlanId == True:
+      print "    vlanId: %s" % vlanId
+    if matchVlanPriority == True:
+      print "    vlanPriority: %s" % vlanPriority
+    if matchEthernetFrameType == True:
+      print "    ethernetFrameType: %s" % ethernetFrameType
+    if matchIpToS == True:
+      print "    ipToS: %s" % ipToS
+    if matchIpProto == True:
+      print "    ipProto: %s" % ipProto
+    if matchSrcIPv4Net == True:
+      print "    srcIPv4Net: %s" % srcIPv4Net['value']
+    if matchDstIPv4Net == True:
+      print "    dstIPv4Net: %s" % dstIPv4Net['value']
+    if matchSrcTcpUdpPort == True:
+      print "    srcTcpUdpPort: %s" % srcTcpUdpPort
+    if matchDstTcpUdpPort == True:
+      print "    dstTcpUdpPort: %s" % dstTcpUdpPort
+
+    for a in actions:
+      actionType = a['actionType']
+      if actionType == "ACTION_OUTPUT":
+	port = a['actionOutput']['port']
+	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)
+
+def get_flow_path(flow_id):
+  try:
+    command = "curl -s \"http://%s:%s/wm/flow/get/%s/json\"" % (ControllerIP, ControllerPort, flow_id)
+    debug("get_flow_path %s" % command)
+
+    result = os.popen(command).read()
+    debug("result %s" % result)
+    if len(result) == 0:
+	print "No Flow found"
+	return;
+
+    parsedResult = json.loads(result)
+    debug("parsed %s" % parsedResult)
+  except:
+    log_error("Controller IF has issue")
+    exit(1)
+
+  print_flow_path(parsedResult)
+
+
+def get_installer_flow_paths(installer_id, v1, p1, v2, p2):
+  try:
+    command = "curl -s \"http://%s:%s/wm/flow/getall-by-installer-id/%s/%s/%s/%s/%s/json\"" % (ControllerIP, ControllerPort, installer_id, v1, p1, v2, p2)
+    debug("get_installer_flow_paths %s" % command)
+
+    result = os.popen(command).read()
+    debug("result %s" % result)
+    if len(result) == 0:
+	print "No Flows found"
+	return;
+
+    parsedResult = json.loads(result)
+    debug("parsed %s" % parsedResult)
+  except:
+    log_error("Controller IF has issue")
+    exit(1)
+
+  for flowPath in parsedResult:
+    print_flow_path(flowPath)
+
+
+def get_endpoints_flow_paths(v1, p1, v2, p2):
+  try:
+    command = "curl -s \"http://%s:%s/wm/flow/getall-by-endpoints/%s/%s/%s/%s/json\"" % (ControllerIP, ControllerPort, v1, p1, v2, p2)
+    debug("get_endpoints_flow_paths %s" % command)
+
+    result = os.popen(command).read()
+    debug("result %s" % result)
+    if len(result) == 0:
+	print "No Flows found"
+	return;
+
+    parsedResult = json.loads(result)
+    debug("parsed %s" % parsedResult)
+  except:
+    log_error("Controller IF has issue")
+    exit(1)
+
+  for flowPath in parsedResult:
+    print_flow_path(flowPath)
+
+
+def get_all_flow_paths():
+  try:
+    command = "curl -s \"http://%s:%s/wm/flow/getall/json\"" % (ControllerIP, ControllerPort)
+    debug("get_all_flow_paths %s" % command)
+
+    result = os.popen(command).read()
+    debug("result %s" % result)
+    if len(result) == 0:
+	print "No Flows found"
+	return;
+
+    parsedResult = json.loads(result)
+    debug("parsed %s" % parsedResult)
+  except:
+    log_error("Controller IF has issue")
+    exit(1)
+
+  for flowPath in parsedResult:
+    print_flow_path(flowPath)
+
+if __name__ == "__main__":
+  usage_msg1 = "Usage:\n"
+  usage_msg2 = "%s <flow_id> : Print flow with Flow ID of <flow_id>\n" % (sys.argv[0])
+  usage_msg3 = "                   all    : Print all flows\n"
+  usage_msg4 = "                   installer <installer-id> <src-dpid> <src-port> <dest-dpid> <dest-port>\n"
+  usage_msg5 = "                   endpoints <src-dpid> <src-port> <dest-dpid> <dest-port>"
+  usage_msg = usage_msg1 + usage_msg2 + usage_msg3 + usage_msg4 + usage_msg5;
+
+  # 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)
+
+  # Do the work
+  if sys.argv[1] == "all":
+    get_all_flow_paths()
+  elif sys.argv[1] == "installer":
+    if len(sys.argv) < 7:
+      log_error(usage_msg)
+      exit(1)
+    get_installer_flow_paths(sys.argv[2], sys.argv[3], sys.argv[4],
+			     sys.argv[5], sys.argv[6])
+  elif sys.argv[1] == "endpoints":
+    if len(sys.argv) < 6:
+      log_error(usage_msg)
+      exit(1)
+    get_endpoints_flow_paths(sys.argv[2], sys.argv[3], sys.argv[4],
+			     sys.argv[5])
+  else:
+    get_flow_path(sys.argv[1])
diff --git a/web/js/controller-status.js b/web/js/controller-status.js
index 75cc5cc..1cca38f 100644
--- a/web/js/controller-status.js
+++ b/web/js/controller-status.js
@@ -24,6 +24,15 @@
 	attr("height", 50);
 
     d3.json(data_source, draw);
+    setInterval(function() {
+        $.ajax({
+	    url: data_source,
+	    success: function(json) {
+		draw(json)
+	    },
+	    dataType: "json"
+        });
+    }, 5000); 
 
     function draw(json){
 //	var data = json.data;
@@ -159,15 +168,6 @@
 	cassandra_rect.exit().remove();
 	cassandra_text.exit().remove();
 
-	setInterval(function() {
-            $.ajax({
-		url: data_source,
-		success: function(json) {
-		    draw(json)
-		},
-		dataType: "json"
-            });
-	}, 3000); 
     }
 /*
     $("#more").click( function() {
diff --git a/web/js/onos-topology-route.js b/web/js/onos-topology-route.js
index 3b13b9c..cca644b 100644
--- a/web/js/onos-topology-route.js
+++ b/web/js/onos-topology-route.js
@@ -374,8 +374,7 @@
 	    return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
 	});
 
-	path
-	    .attr("stroke", function(d) {
+	path.attr("stroke", function(d) {
 	    if(d.type == 1){
 		return "red"
 	    } else {
diff --git a/web/js/onos-topology.js b/web/js/onos-topology.js
index 8573f7c..062d37b 100644
--- a/web/js/onos-topology.js
+++ b/web/js/onos-topology.js
@@ -208,23 +208,50 @@
 		}
 	    }
 	}
+	for (var i = 0; i < links.length; i++) {
+            for (var j = 0; j < json.links.length; j++) {
+		if (links[i].target.name == json.nodes[json.links[j].target].name && 
+		    links[i].source.name == json.nodes[json.links[j].source].name ){
+		    if (links[i].type != json.links[j].type){
+			links[i].type = json.links[j].type;
+			changed = true;
+		    }
+		}
+	    }
+	}
 	return changed
     }
 
     function draw(force, path, circle, text){
 	force.stop();
         path.enter().append("svg:path")
-	    .attr("class", function(d) { return "link"; });
+	    .attr("class", function(d) { return "link"; })
+	    .attr("marker-end", function(d) {
+		if(d.type == 1){
+		    return "url(#TriangleRed)";
+		} else {
+		    return "url(#Triangle)";
+		}
+	    });
 
         circle.enter().append("svg:circle")
-	    .attr("r", radius)
+	    .attr("r", function(d) { 
+		if (d.group == 1000){
+		    return radius/2;
+		}else{
+		    return radius;
+		}
+	    })
 	    .call(node_drag);
 //            .call(force.drag);
 
 	text.enter().append("svg:text")
 	    .attr("x", radius)
 	    .attr("y", ".31em")
-	    .text(function(d) { return d.name.split(":")[5] + d.name.split(":")[6] + d.name.split(":")[7] });
+	    .text(function(d) { 
+		l=d.name.split(":").length
+		return d.name.split(":")[l-3] + ":" + d.name.split(":")[l-2] + ":" + d.name.split(":")[l-1]
+	    });
 
         circle.append("title")
 	    .text(function(d) { return d.name; });
@@ -234,6 +261,7 @@
 	    else if (d.group == 2){return "blue";}
 	    else if (d.group == 3){return "green";}
 	    else if (d.group == 4){return "orange";}
+	    else if (d.group == 1000){return "black";}
 	    else{ return "gray"; }
 	});
 
@@ -245,7 +273,7 @@
 	    }
 	}).attr("stroke-width", function(d) {
 	    if(d.type == 1){
-		return "4px";
+		return "2px";
 	    } else {
 		return "1.5px";
 	    }
@@ -271,13 +299,13 @@
 	var changed = cdiff(json);
 
 	console.log("changed? " + changed);
+        path = svg.selectAll("path").data(links)
+        circle = svg.selectAll("circle").data(nodes);
+	text = svg.selectAll("text").data(nodes);
 
+	console.log(path)
 	if (changed){
 
-            path = svg.selectAll("path").data(links)
-            circle = svg.selectAll("circle").data(nodes);
-	    text = svg.selectAll("text").data(nodes);
-
 	    draw(force, path, circle, text);
 	}
     }
@@ -299,8 +327,6 @@
             });
 	}, 3000); 
     }
-
-
     function tick() {
 	path.attr("d", function(d) {
 	    var dx = d.target.x - d.source.x,
@@ -309,6 +335,26 @@
 	    dr = 0;  // 0 for direct line
 	    return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
 	});
+	path.attr("stroke", function(d) {
+	    if(d.type == 1){
+		return "red"
+	    } else {
+		return "black"
+	    }
+	}).attr("stroke-width", function(d) {
+	    if(d.type == 1){
+		return "3px";
+	    } else {
+		return "1.5px";
+	    }
+	}).attr("marker-end", function(d) {
+	    if(d.type == 1){
+		return "url(#TriangleRed)";
+	    } else {
+		return "url(#Triangle)";
+	    }
+	});
+
 //	circle.attr("cx", function(d) { return d.x; }).attr("cy", function(d) { return d.y; });
 	circle.attr("transform", function(d) {
 	    x = Math.max(radius, Math.min(width - radius, d.x));
@@ -322,6 +368,7 @@
 	    else if (d.group == 2){return "blue";}
 	    else if (d.group == 3){return "green";}
 	    else if (d.group == 4){return "orange";}
+	    else if (d.group == 1000){return "black";}
 	    else{ return "gray"; }
 	});
 //	text.attr("x", function(d) { return d.x; }).attr("y", function(d) { return d.y; });
diff --git a/web/onos-topology-route.html b/web/onos-topology-route.html
index 860a096..6c167c7 100644
--- a/web/onos-topology-route.html
+++ b/web/onos-topology-route.html
@@ -42,7 +42,7 @@
     </marker>
   </defs>
 <script type="text/javascript">
-gui("http://onosnat.onlab.us:8080/wm/topology/toporoute/00:00:00:00:00:a1/2/00:00:00:00:00:c1/3/json");
+gui("http://onosnat.onlab.us:8080/wm/topology/toporoute/00:00:00:0d:00:d1/2/00:00:00:0d:00:d3/3/json");
 </script>
 </svg>
 </body>
diff --git a/web/onos-topology.html b/web/onos-topology.html
index b0614c9..9d90ea4 100644
--- a/web/onos-topology.html
+++ b/web/onos-topology.html
@@ -2,11 +2,6 @@
 <html>
 <meta charset="utf-8">
 <style>
-path.link {
-  fill: none;
-  stroke: #666;
-  stroke-width: 1.5px;
-}
 circle {
   stroke: #333;
   stroke-width: 1.5px;
@@ -40,6 +35,13 @@
       orient="auto">
       <path d="M0,-5L10,0L0,5"/>
     </marker>
+    <marker id="TriangleRed"
+      viewBox="0 -5 10 10" refX="10" refY="-0.2" 
+      markerUnits="strokeWidth"
+      markerWidth="6" markerHeight="6"
+      orient="auto">
+      <path d="M0,-5L10,0L0,5" fill="red" stroke="red"/>
+    </marker>
   </defs>
 <h1>ONOS Sprint 4 Demo GUI</h1>
 <h2>Controller Status</h2>
diff --git a/web/shortest_path.py b/web/shortest_path.py
index 381d052..8a77d61 100755
--- a/web/shortest_path.py
+++ b/web/shortest_path.py
@@ -41,15 +41,21 @@
 def shortest_path(v1, p1, v2, p2):
   try:
     command = "curl -s http://%s:%s/wm/topology/route/%s/%s/%s/%s/json" % (ControllerIP, ControllerPort, v1, p1, v2, p2)
+    debug("shortest_path %s" % command)
+
     result = os.popen(command).read()
+    debug("result %s" % result)
+
+    if len(result) == 0:
+	print "No Path found"
+	return;
+
     parsedResult = json.loads(result)
+    debug("parsed %s" % parsedResult)
   except:
     log_error("Controller IF has issue")
     exit(1)
 
-  debug("shortest_path %s" % command)
-  debug("parsed %s" % parsedResult)
-
   srcSwitch = parsedResult['srcPort']['dpid']['value'];
   srcPort = parsedResult['srcPort']['port']['value'];
   dstSwitch = parsedResult['dstPort']['dpid']['value'];
diff --git a/web/topology_rest.py b/web/topology_rest.py
index 5eaed03..b109ccc 100755
--- a/web/topology_rest.py
+++ b/web/topology_rest.py
@@ -114,6 +114,7 @@
   topo = {}
   switches = []
   links = []
+  devices = []
 
   for v in parsedResult:
     if v.has_key('dpid'):
@@ -123,19 +124,45 @@
       sw = {}
       sw['name']=dpid
       sw['group']= -1
-      if state == "ACTIVE":
-        if dpid.split(":")[5] == "0a":
-          sw['group']=1
-        if dpid.split(":")[5] == "0b":
-          sw['group']=2
-        if dpid.split(":")[5] == "0c":
-          sw['group']=3
-        if dpid.split(":")[5] == "0d":
-          sw['group']=4
+
+#      if state == "ACTIVE":
+#        if dpid.split(":")[5] == "0a":
+#          sw['group']=1
+#        if dpid.split(":")[5] == "0b":
+#          sw['group']=2
+#        if dpid.split(":")[5] == "0c":
+#          sw['group']=3
+#        if dpid.split(":")[5] == "0d":
+#          sw['group']=4
       if state == "INACTIVE":
         sw['group']=0
       switches.append(sw)
 
+## Comment in if we need devies
+#      sw_index = len(switches) - 1
+#      for p in v['ports']:
+#        for d in p['devices']:
+#          device = {}
+#          device['attached_switch']=dpid
+#          device['name']=d['mac']
+#          if d['state'] == "ACTIVE":
+#            device['group']=1000
+#          else:
+#            device['group']=1001
+#
+#          switches.append(device)
+#          device_index = len (switches) -1
+#          link = {}
+#          link['source'] = device_index
+#          link['target'] = sw_index
+#          link['type'] = -1
+#          links.append(link)
+#          link = {}
+#          link['source'] = sw_index
+#          link['target'] = device_index
+#          link['type'] = -1
+#          links.append(link)
+
 #  try:
 #    command = "curl -s \'http://%s:%s/wm/registry/controllers/json\'" % (RestIP, RestPort)
 #    result = os.popen(command).read()
@@ -162,23 +189,26 @@
 
   try:
     v1 = "00:00:00:00:00:0a:0d:00"
+#    v1 = "00:00:00:00:00:0d:00:d1"
     p1=1
     v2 = "00:00:00:00:00:0b:0d:03"
-    p1=2
+#    v2 = "00:00:00:00:00:0d:00:d3"
+    p2=1
     command = "curl -s http://%s:%s/wm/topology/route/%s/%s/%s/%s/json" % (RestIP, RestPort, v1, p1, v2, p2)
     result = os.popen(command).read()
     parsedResult = json.loads(result)
   except:
     log_error("No route")
-    parsedResult = []
+    parsedResult = {}
 
-  path = [];    
-  for i, v in enumerate(parsedResult):
-    if i < len(parsedResult) - 1:
-      sdpid= parsedResult[i]['switch']
-      ddpid = parsedResult[i+1]['switch']
-      path.append( (sdpid, ddpid))  
-
+  path = []
+  if parsedResult.has_key('flowEntries'):
+    flowEntries= parsedResult['flowEntries']
+    for i, v in enumerate(flowEntries):
+      if i < len(flowEntries) - 1:
+        sdpid= flowEntries[i]['dpid']['value']
+        ddpid = flowEntries[i+1]['dpid']['value']
+        path.append( (sdpid, ddpid))  
 
   try:
     command = "curl -s \'http://%s:%s/wm/core/topology/links/json\'" % (RestIP, RestPort)
@@ -212,12 +242,11 @@
   topo['nodes'] = switches
   topo['links'] = links
 
-#  pp.pprint(topo)
+  pp.pprint(topo)
   js = json.dumps(topo)
   resp = Response(js, status=200, mimetype='application/json')
   return resp
 
-
 #@app.route("/wm/topology/toporoute/00:00:00:00:00:a1/2/00:00:00:00:00:c1/3/json")
 #@app.route("/wm/topology/toporoute/<srcdpid>/<srcport>/<destdpid>/<destport>/json")
 @app.route("/wm/topology/toporoute/<v1>/<p1>/<v2>/<p2>/json")