Merge branch 'master' of https://github.com/OPENNETWORKINGLAB/ONOS
diff --git a/README.md b/README.md
index 495a2ed..516091e 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,12 @@
-ONOS
-====
+ONOS (Open Networking Operating System)
+=======================================
 
-Open Networking Operating System
+ONOS (Open Network Operating System) is an experimental distributed SDN OS. ONOS is under development. ONOS was announced and demonstrated at ONS'13.
 
-BELOW TO BE WRITTEN IN DETAIL
+Steps to download and setup development VM
+==========================================
 
+http://wiki.onlab.us/display/Eng/ONOS+Development+VM
 
 Building ONOS
 -------------
diff --git a/pom.xml b/pom.xml
index 52fe30a..44b7f07 100644
--- a/pom.xml
+++ b/pom.xml
@@ -121,6 +121,30 @@
           <locale>en</locale>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <version>0.6.3.201306030806</version>
+        <configuration>
+          <destfile>${basedir}/target/jacoco/jacoco.exec</destfile>
+          <datafile>${basedir}/target/jacoco/jacoco.exec</datafile>
+        </configuration>
+        <executions>
+          <execution>
+            <id>jacoco-initialize</id>
+            <goals>
+              <goal>prepare-agent</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>jacoco-site</id>
+            <phase>package</phase>
+            <goals>
+              <goal>report</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
   <!-- for getting visualization reporting   -->
@@ -145,6 +169,7 @@
           <reportSet>
             <reports>
               <report>dependencies</report>
+              <report>maven-emma-plugin</report>
               <report>scm</report>
             </reports>
           </reportSet>
diff --git a/src/main/java/net/onrc/onos/flow/FlowManagerImpl.java b/src/main/java/net/onrc/onos/flow/FlowManagerImpl.java
index 1f041e6..2e2706c 100644
--- a/src/main/java/net/onrc/onos/flow/FlowManagerImpl.java
+++ b/src/main/java/net/onrc/onos/flow/FlowManagerImpl.java
@@ -28,6 +28,7 @@
 import net.onrc.onos.ofcontroller.util.Dpid;
 import net.onrc.onos.ofcontroller.util.FlowEntry;
 import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+import net.onrc.onos.ofcontroller.util.FlowEntryActions;
 import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
 import net.onrc.onos.ofcontroller.util.FlowPath;
 import net.onrc.onos.ofcontroller.util.Port;
@@ -132,16 +133,12 @@
 		    flowEntry.setOutPort(new Port(src_port.getNumber()));
 		    flowEntry.setFlowEntryMatch(new FlowEntryMatch());
 		    flowEntry.flowEntryMatch().enableInPort(flowEntry.inPort());
-		    
+
 		    // Set the outgoing port output action
-		    ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
-		    if (flowEntryActions == null) {
-			flowEntryActions = new ArrayList<FlowEntryAction>();
-			flowEntry.setFlowEntryActions(flowEntryActions);
-		    }
+		    FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
 		    FlowEntryAction flowEntryAction = new FlowEntryAction();
 		    flowEntryAction.setActionOutput(flowEntry.outPort());
-		    flowEntryActions.add(flowEntryAction);
+		    flowEntryActions.addAction(flowEntryAction);
 		    dataPath.flowEntries().add(flowEntry);
 		    
 		    FlowPath flowPath = new FlowPath();
@@ -254,14 +251,10 @@
 		    flowEntry.flowEntryMatch().enableInPort(flowEntry.inPort());
 		    
 		    // Set the outgoing port output action
-		    ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
-		    if (flowEntryActions == null) {
-			flowEntryActions = new ArrayList<FlowEntryAction>();
-			flowEntry.setFlowEntryActions(flowEntryActions);
-		    }
+		    FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
 		    FlowEntryAction flowEntryAction = new FlowEntryAction();
 		    flowEntryAction.setActionOutput(flowEntry.outPort());
-		    flowEntryActions.add(flowEntryAction);
+		    flowEntryActions.addAction(flowEntryAction);
 		    dataPath.flowEntries().add(flowEntry);
 			continue;
 		    }
@@ -276,14 +269,10 @@
 		    flowEntry.flowEntryMatch().enableInPort(flowEntry.inPort());
 		    
 		    // Set the outgoing port output action
-		    ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
-		    if (flowEntryActions == null) {
-			flowEntryActions = new ArrayList<FlowEntryAction>();
-			flowEntry.setFlowEntryActions(flowEntryActions);
-		    }
+		    FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
 		    FlowEntryAction flowEntryAction = new FlowEntryAction();
 		    flowEntryAction.setActionOutput(flowEntry.outPort());
-		    flowEntryActions.add(flowEntryAction);
+		    flowEntryActions.addAction(flowEntryAction);
 		    dataPath.flowEntries().add(flowEntry);
 		    dataPath.flowEntries().add(flowEntry);
 		}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
index 2034118..acdf185 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
@@ -10,7 +10,10 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
@@ -61,6 +64,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
 public class BgpRoute implements IFloodlightModule, IBgpRouteService, 
 									ITopologyListener, IOFSwitchListener {
 	
@@ -75,6 +80,8 @@
 	protected ProxyArpManager proxyArp;
 	
 	protected static Ptree ptree;
+	protected BlockingQueue<RibUpdate> ribUpdates;
+	
 	protected String bgpdRestIp;
 	protected String routerId;
 	protected String configFilename = "config.json";
@@ -119,9 +126,9 @@
 				ITopoLinkService topoLinkService = new TopoLinkServiceImpl();
 				
 				List<Link> activeLinks = topoLinkService.getActiveLinks();
-				for (Link l : activeLinks){
-					log.debug("active link: {}", l);
-				}
+				//for (Link l : activeLinks){
+					//log.debug("active link: {}", l);
+				//}
 				
 				Iterator<LDUpdate> it = linkUpdates.iterator();
 				while (it.hasNext()){
@@ -139,12 +146,12 @@
 			if (linkUpdates.isEmpty()){
 				//All updates have been seen in network map.
 				//We can check if topology is ready
-				log.debug("No know changes outstanding. Checking topology now");
+				log.debug("No known changes outstanding. Checking topology now");
 				checkStatus();
 			}
 			else {
 				//We know of some link updates that haven't propagated to the database yet
-				log.debug("Some changes not found in network map- size {}", linkUpdates.size());
+				log.debug("Some changes not found in network map - {} links missing", linkUpdates.size());
 				topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
 			}
 		}
@@ -215,6 +222,8 @@
 			throws FloodlightModuleException {
 	    
 	    ptree = new Ptree(32);
+	    
+	    ribUpdates = new LinkedBlockingQueue<RibUpdate>();
 	    	
 		// Register floodlight provider and REST handler.
 		floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
@@ -224,7 +233,7 @@
 		
 		//TODO We'll initialise this here for now, but it should really be done as
 		//part of the controller core
-		proxyArp = new ProxyArpManager(floodlightProvider, topology);
+		proxyArp = new ProxyArpManager(floodlightProvider, topology, devices);
 		
 		linkUpdates = new ArrayList<LDUpdate>();
 		ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
@@ -311,23 +320,23 @@
 		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);
+		ptree.acquire(p.getAddress(), p.getPrefixLength());
+		ptree.acquire(q.getAddress(), q.getPrefixLength());
+		ptree.acquire(r.getAddress(), r.getPrefixLength());
 	
 		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);
+		PtreeNode n = ptree.match(a.getAddress(), a.getPrefixLength());
 		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);
+		n = ptree.lookup(p.getAddress(), p.getPrefixLength());
 		if (n != null) {
 			ptree.delReference(n);
 			ptree.delReference(n);
@@ -337,7 +346,7 @@
 			Prefix p_result = new Prefix(node.key, node.keyBits);
 		}
 		
-		n = ptree.lookup(q.getAddress(), q.masklen);
+		n = ptree.lookup(q.getAddress(), q.getPrefixLength());
 		if (n != null) {
 			ptree.delReference(n);
 			ptree.delReference(n);
@@ -347,7 +356,7 @@
 			Prefix p_result = new Prefix(node.key, node.keyBits);
 		}
 		
-		n = ptree.lookup(r.getAddress(), r.masklen);
+		n = ptree.lookup(r.getAddress(), r.getPrefixLength());
 		if (n != null) {
 			ptree.delReference(n);
 			ptree.delReference(n);
@@ -409,8 +418,8 @@
 				continue;
 			}
 			
-			PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
-			Rib rib = new Rib(router_id, nexthop, p.masklen);
+			PtreeNode node = ptree.acquire(p.getAddress(), p.getPrefixLength());
+			Rib rib = new Rib(router_id, nexthop, p.getPrefixLength());
 			
 			if (node.rib != null) {
 				node.rib = null;
@@ -423,6 +432,53 @@
 		} 
 	}
 	
+	@Override
+	public void newRibUpdate(RibUpdate update) {
+		ribUpdates.add(update);
+	}
+	
+	//TODO temporary
+	public void wrapPrefixAdded(RibUpdate update) {
+		Prefix prefix = update.getPrefix();
+		
+		PtreeNode node = ptree.acquire(prefix.getAddress(), prefix.getPrefixLength());
+		
+		if (node.rib != null) {
+			node.rib = null;
+			ptree.delReference(node);
+		}
+		node.rib = update.getRibEntry();
+
+		prefixAdded(node);
+	}
+	
+	//TODO temporary
+	public void wrapPrefixDeleted(RibUpdate update) {
+		Prefix prefix = update.getPrefix();
+		
+		PtreeNode node = ptree.lookup(prefix.getAddress(), prefix.getPrefixLength());
+		
+		/* 
+		 * Remove the flows from the switches before the rib is lost
+		 * Theory: we could get a delete for a prefix not in the Ptree.
+		 * This would result in a null node being returned. We could get a delete for
+		 * a node that's not actually there, but is a aggregate node. This would result
+		 * in a non-null node with a null rib. Only a non-null node with a non-null
+		 * rib is an actual prefix in the Ptree.
+		 */
+		if (node != null && node.rib != null){
+			prefixDeleted(node);
+		}
+
+		if (node != null && node.rib != null) {
+			if (update.getRibEntry().equals(node.rib)) {
+				node.rib = null;
+				ptree.delReference(node);					
+			}
+		}
+	}
+	
+	@Override
 	public void prefixAdded(PtreeNode node) {
 		if (!topologyReady){
 			return;
@@ -435,7 +491,7 @@
 				node.rib.routerId.getHostAddress()});
 		
 		//TODO this is wrong, we shouldn't be dealing with BGP peers here.
-		//We need to figure out where the device is attached and what it's
+		//We need to figure out where the device is attached and what its
 		//mac address is by learning. 
 		//The next hop is not necessarily the peer, and the peer's attachment
 		//point is not necessarily the next hop's attachment point.
@@ -549,6 +605,7 @@
 	}
 	
 	//TODO this is largely untested
+	@Override
 	public void prefixDeleted(PtreeNode node) {
 		if (!topologyReady) {
 			return;
@@ -915,9 +972,45 @@
 		
 		floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
 		
+		ExecutorService e = Executors.newSingleThreadExecutor(
+				new ThreadFactoryBuilder().setNameFormat("bgp-updates-%d").build());
+		
+		
+		e.execute(new Runnable() {
+			@Override
+			public void run() {
+				doUpdatesThread();
+			}
+		});
+		
 		//Retrieve the RIB from BGPd during startup
 		retrieveRib();
 	}
+	
+	private void doUpdatesThread() {
+		boolean interrupted = false;
+		try {
+			while (true) {
+				try {
+					RibUpdate update = ribUpdates.take();
+					switch (update.getOperation()){
+					case UPDATE:
+						wrapPrefixAdded(update);
+						break;
+					case DELETE:
+						wrapPrefixDeleted(update);
+						break;
+					}
+				} catch (InterruptedException e) {
+					interrupted = true;
+				}
+			}
+		} finally {
+			if (interrupted) {
+				Thread.currentThread().interrupt();
+			}
+		}
+	}
 
 	@Override
 	public void topologyChanged() {		
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
index 8355308..19b44c8 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
@@ -2,6 +2,8 @@
 
 import java.net.UnknownHostException;
 
+import net.onrc.onos.ofcontroller.bgproute.RibUpdate.Operation;
+
 import org.restlet.resource.Delete;
 import org.restlet.resource.Get;
 import org.restlet.resource.Post;
@@ -100,7 +102,7 @@
 		IBgpRouteService bgpRoute = (IBgpRouteService) getContext().getAttributes().
 				get(IBgpRouteService.class.getCanonicalName());
 
-		Ptree ptree = bgpRoute.getPtree();
+		//Ptree ptree = bgpRoute.getPtree();
 
 		String routerId = (String) getRequestAttributes().get("routerid");
 		String prefix = (String) getRequestAttributes().get("prefix");
@@ -125,9 +127,13 @@
 				return reply + "\n";
 			}
 			
-			PtreeNode node = ptree.acquire(p.getAddress(), p.masklen);
-			Rib rib = new Rib(routerId, nexthop, p.masklen);
+			Rib rib = new Rib(routerId, nexthop, p.getPrefixLength());
 
+			bgpRoute.newRibUpdate(new RibUpdate(Operation.UPDATE, p, rib));
+			
+			/*
+			PtreeNode node = ptree.acquire(p.getAddress(), p.getPrefixLength());
+			
 			if (node.rib != null) {
 				node.rib = null;
 				ptree.delReference(node);
@@ -135,6 +141,7 @@
 			node.rib = rib;
 
 			bgpRoute.prefixAdded(node);
+			*/
 			
 			reply = "[POST: " + prefix + "/" + mask + ":" + nexthop + "]";
 			log.info(reply);
@@ -158,7 +165,7 @@
 		IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
 				get(IBgpRouteService.class.getCanonicalName());
 
-		Ptree ptree = bgpRoute.getPtree();
+		//Ptree ptree = bgpRoute.getPtree();
 
 		String routerId = (String) getRequestAttributes().get("routerid");
 		String prefix = (String) getRequestAttributes().get("prefix");
@@ -182,8 +189,13 @@
 				log.info(reply);
 				return reply + "\n";
 			}
-
-			PtreeNode node = ptree.lookup(p.getAddress(), p.masklen);
+			
+			Rib r = new Rib(routerId, nextHop, p.getPrefixLength());
+			
+			bgpRoute.newRibUpdate(new RibUpdate(Operation.DELETE, p, r));
+			
+			/*
+			PtreeNode node = ptree.lookup(p.getAddress(), p.getPrefixLength());
 			
 			//Remove the flows from the switches before the rib is lost
 			//Theory: we could get a delete for a prefix not in the Ptree.
@@ -195,7 +207,7 @@
 				bgpRoute.prefixDeleted(node);
 			}
 
-			Rib r = new Rib(routerId, nextHop, p.masklen);
+			
 
 			if (node != null && node.rib != null) {
 				if (r.equals(node.rib)) {
@@ -203,6 +215,7 @@
 					ptree.delReference(node);					
 				}
 			}
+			*/
 			
 			reply =reply + "[DELE: " + prefix + "/" + mask + ":" + nextHop + "]";
 		}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
index c84a415..3dbc940 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
@@ -14,6 +14,12 @@
 
 	public void clearPtree();
 	
+	/**
+	 * Pass a RIB update to the {@link IBgpRouteService}
+	 * @param update
+	 */
+	public void newRibUpdate(RibUpdate update);
+	
 	//TODO This functionality should be provided by some sort of Ptree listener framework
 	public void prefixAdded(PtreeNode node);
 	public void prefixDeleted(PtreeNode node);
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
index c3baa37..4d7c53a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
@@ -4,31 +4,40 @@
 import java.net.UnknownHostException;
 
 public class Prefix {
-	public int masklen;
-	protected InetAddress address;
+	private int prefixLength;
+	private InetAddress address;
 
-	public Prefix(byte[] addr, int masklen) throws UnknownHostException {
+	public Prefix(byte[] addr, int prefixLength) throws UnknownHostException {
 		//try {
 		address = InetAddress.getByAddress(addr);
 		//} catch (UnknownHostException e) {
 		//	System.out.println("InetAddress exception");
 		//	return;
 		//}
-		this.masklen = masklen;
-		//System.out.println(address.toString() + "/" + masklen);
+		this.prefixLength = prefixLength;
+		//System.out.println(address.toString() + "/" + prefixLength);
 	}
 
-	public Prefix(String str, int masklen) throws UnknownHostException {
+	public Prefix(String str, int prefixLength) throws UnknownHostException {
 		//try {
 		address = InetAddress.getByName(str);
 		//} catch (UnknownHostException e) {
 		//	System.out.println("InetAddress exception");
 		//	return;
 		//}
-		this.masklen = masklen;
+		this.prefixLength = prefixLength;
 	}
 
-	public byte [] getAddress() {
+	public int getPrefixLength() {
+		return prefixLength;
+	}
+	
+	public byte[] getAddress() {
 		return address.getAddress();
 	}
+	
+	@Override
+	public String toString() {
+		return address.getHostAddress() + "/" + prefixLength;
+	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Ptree.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Ptree.java
index 6741a41..041061c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Ptree.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Ptree.java
@@ -1,24 +1,33 @@
 package net.onrc.onos.ofcontroller.bgproute;
 
+/*
+ * TODO This Ptree needs to be refactored if we're going to use it permenantly.
+ *
+ * The biggest problem is it leaks PTreeNode references - these need to stay within
+ * the Ptree as they contain data fundamental to the structure of the tree.
+ * You should put RIB entries in and get RIB entries out.
+ * Also we need to get rid of the referencing scheme to determine when to delete nodes.
+ * Deletes should be explicit, and there's no need to keep track of references if 
+ * we don't leak them out the the Ptree.
+ */
 public class Ptree {
-	int maxKeyBits;
-	int maxKeyOctets;
-	int refCount;
-	PtreeNode top;
-	byte maskBits[] = { (byte)0x00, (byte)0x80, (byte)0xc0, (byte)0xe0, (byte)0xf0, (byte)0xf8, (byte)0xfc, (byte)0xfe, (byte)0xff };
+	private int maxKeyBits;
+	private int maxKeyOctets;
+	//private int refCount;
+	private PtreeNode top;
+	private byte maskBits[] = { (byte)0x00, (byte)0x80, (byte)0xc0, (byte)0xe0, (byte)0xf0, (byte)0xf8, (byte)0xfc, (byte)0xfe, (byte)0xff };
 	
-	// Constructor.
-	Ptree(int max_key_bits) {
+	public Ptree(int max_key_bits) {
 		maxKeyBits = max_key_bits;
 		maxKeyOctets = bit_to_octet(max_key_bits); 
-		refCount = 0;
+		//refCount = 0;
 	}
 	
-	public PtreeNode acquire(byte [] key) {
+	public synchronized PtreeNode acquire(byte [] key) {
 		return acquire(key, maxKeyBits);
 	}
 	
-	public PtreeNode acquire(byte [] key, int key_bits) {
+	public synchronized PtreeNode acquire(byte [] key, int key_bits) {
 		if (key_bits > maxKeyBits) {
 			return null;
 		}
@@ -76,7 +85,7 @@
 		return addReference(add);
 	}
 
-	public PtreeNode lookup(byte [] key, int key_bits) {
+	public synchronized PtreeNode lookup(byte [] key, int key_bits) {
 		if (key_bits > maxKeyBits) {
 			return null;
 		}
@@ -99,7 +108,7 @@
 		return null;
 	}
 	
-	public PtreeNode match(byte [] key, int key_bits) {
+	public synchronized PtreeNode match(byte [] key, int key_bits) {
 		if (key_bits > maxKeyBits) {
 			return null;
 		}
@@ -127,14 +136,14 @@
 		return null;
 	}
 	
-	public PtreeNode begin() {
+	public synchronized PtreeNode begin() {
 		if (top == null) {
 			return null;
 		}
 		return addReference(top);
 	}
 	
-	public PtreeNode next(PtreeNode node) {
+	public synchronized PtreeNode next(PtreeNode node) {
 		PtreeNode next;
 		
 		if (node.left != null) {
@@ -175,7 +184,7 @@
 		return node;
 	}
 	
-	public void delReference(PtreeNode node) {
+	public synchronized void delReference(PtreeNode node) {
 		if (node.refCount > 0) {
 			node.refCount--;
 		}
@@ -278,10 +287,6 @@
 		
 		return add;
 	}
-	//add by linpp
-	private void clear() {
-	
-	}
 	
 	private void node_remove(PtreeNode node) {
 		PtreeNode child;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibUpdate.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibUpdate.java
new file mode 100644
index 0000000..c7272bd
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibUpdate.java
@@ -0,0 +1,27 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+public class RibUpdate {
+	public enum Operation {UPDATE, DELETE}; 
+	
+	private Operation operation;
+	private Prefix prefix;
+	private Rib ribEntry;
+	
+	public RibUpdate(Operation operation, Prefix prefix, Rib ribEntry) {
+		this.operation = operation;
+		this.prefix = prefix;
+		this.ribEntry = ribEntry;
+	}
+
+	public Operation getOperation() {
+		return operation;
+	}
+
+	public Prefix getPrefix() {
+		return prefix;
+	}
+
+	public Rib getRibEntry() {
+		return ribEntry;
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
index 2672a6b..9a96638 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjects.java
@@ -253,6 +253,9 @@
 		@Adjacency(label="flow", direction=Direction.IN)
 		public void removeFlowEntry(final IFlowEntry flowEntry);
 
+		//
+		// Matching fields
+		//
 		@JsonIgnore
 		@Property("matchSrcMac")
 		public String getMatchSrcMac();
@@ -330,6 +333,18 @@
 		@Property("matchDstTcpUdpPort")
 		public void setMatchDstTcpUdpPort(Short matchDstTcpUdpPort);
 
+		//
+		// Action-related fields
+		//
+		@Property("actions")
+		public String getActions();
+
+		@Property("actions")
+		public void setActions(String actionsStr);
+
+		//
+		// Other fields
+		//
 		@JsonIgnore
 		@GremlinGroovy("it.in('flow').out('switch')")
 		public Iterable<ISwitchObject> getSwitches();
@@ -383,6 +398,9 @@
 		@Property("error_state_code")
 		public void setErrorStateCode(String errorStateCode);
 
+		//
+		// Matching fields
+		//
 		@Property("matchInPort")
 		public Short getMatchInPort();
 
@@ -455,12 +473,24 @@
 		@Property("matchDstTcpUdpPort")
 		public void setMatchDstTcpUdpPort(Short matchDstTcpUdpPort);
 
-		@Property("actionOutput")
-		public Short getActionOutput();
+		//
+		// Action-related fields
+		//
+		@Property("actionOutputPort")
+		public Short getActionOutputPort();
 
-		@Property("actionOutput")
-		public void setActionOutput(Short actionOutput);
+		@Property("actionOutputPort")
+		public void setActionOutputPort(Short actionOutputPort);
 
+		@Property("actions")
+		public String getActions();
+
+		@Property("actions")
+		public void setActions(String actionsStr);
+
+		//
+		// Other fields
+		//
 		@Adjacency(label="flow")
 		public IFlowPath getFlow();
 
diff --git a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
index 8c83a7c..e0ac4e1 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/floodlightlistener/NetworkGraphPublisher.java
@@ -127,19 +127,22 @@
 	public void linkDiscoveryUpdate(LDUpdate update) {
 		// TODO Auto-generated method stub
 		Link lt = new Link(update.getSrc(),update.getSrcPort(),update.getDst(),update.getDstPort());
-		log.debug("{}:LinkDicoveryUpdate(): Updating Link {}",this.getClass(), lt);
-		switch (update.getOperation()) {
+		//log.debug("{}:LinkDicoveryUpdate(): Updating Link {}",this.getClass(), lt);
 		
+		switch (update.getOperation()) {
 			case LINK_REMOVED:
+				log.debug("LinkDiscoveryUpdate(): Removing link {}", lt);
 				linkStore.update(lt, DM_OPERATION.DELETE);
 				// TODO: Move network map link removal here
 				// reconcile paths here
 //				IPortObject srcPort = conn.utils().searchPort(conn, HexString.toHexString(update.getSrc()), update.getSrcPort());
 				break;
 			case LINK_UPDATED:
+				log.debug("LinkDiscoveryUpdate(): Updating link {}", lt);
 				linkStore.update(lt, DM_OPERATION.UPDATE);
 				break;
 			case LINK_ADDED:
+				log.debug("LinkDiscoveryUpdate(): Adding link {}", lt);
 				linkStore.update(lt, DM_OPERATION.INSERT);
 				break;
 					
diff --git a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
index 969d41e..a072882 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/flowmanager/FlowManager.java
@@ -42,6 +42,8 @@
 import net.onrc.onos.ofcontroller.util.Dpid;
 import net.onrc.onos.ofcontroller.util.FlowEntry;
 import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+import net.onrc.onos.ofcontroller.util.FlowEntryAction.*;
+import net.onrc.onos.ofcontroller.util.FlowEntryActions;
 import net.onrc.onos.ofcontroller.util.FlowEntryId;
 import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
 import net.onrc.onos.ofcontroller.util.FlowEntrySwitchState;
@@ -58,8 +60,7 @@
 import org.openflow.protocol.OFPacketOut;
 import org.openflow.protocol.OFPort;
 import org.openflow.protocol.OFType;
-import org.openflow.protocol.action.OFAction;
-import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.protocol.action.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -587,6 +588,7 @@
 	// - flowPath.matchIpToS()
 	// - flowPath.matchSrcTcpUdpPort()
 	// - flowPath.matchDstTcpUdpPort()
+	// - flowPath.flowEntryActions()
 	//
 	flowObj.setInstallerId(flowPath.installerId().toString());
 	flowObj.setFlowPathFlags(flowPath.flowPathFlags().flags());
@@ -627,6 +629,9 @@
 	if (flowPath.flowEntryMatch().matchDstTcpUdpPort()) {
 	    flowObj.setMatchDstTcpUdpPort(flowPath.flowEntryMatch().dstTcpUdpPort());
 	}
+	if (! flowPath.flowEntryActions().actions().isEmpty()) {
+	    flowObj.setActions(flowPath.flowEntryActions().toString());
+	}
 
 	if (dataPathSummaryStr != null) {
 	    flowObj.setDataPathSummary(dataPathSummaryStr);
@@ -720,8 +725,6 @@
 	// - InPort edge
 	// - OutPort edge
 	//
-	// - flowEntry.flowEntryMatch()
-	// - flowEntry.flowEntryActions()
 	// - flowEntry.dpid()
 	// - flowEntry.flowEntryUserState()
 	// - flowEntry.flowEntrySwitchState()
@@ -738,7 +741,8 @@
 	// - flowEntry.matchIpToS()
 	// - flowEntry.matchSrcTcpUdpPort()
 	// - flowEntry.matchDstTcpUdpPort()
-	// - flowEntry.actionOutput()
+	// - flowEntry.actionOutputPort()
+	// - flowEntry.actions()
 	//
 	ISwitchObject sw =
 	    op.searchSwitch(flowEntry.dpid().toString());
@@ -785,15 +789,19 @@
 	    flowEntryObj.setMatchDstTcpUdpPort(flowEntry.flowEntryMatch().dstTcpUdpPort());
 	}
 
-	for (FlowEntryAction fa : flowEntry.flowEntryActions()) {
+	for (FlowEntryAction fa : flowEntry.flowEntryActions().actions()) {
 	    if (fa.actionOutput() != null) {
 		IPortObject outport =
 		    op.searchPort(flowEntry.dpid().toString(),
 					      fa.actionOutput().port().value());
-		flowEntryObj.setActionOutput(fa.actionOutput().port().value());
+		flowEntryObj.setActionOutputPort(fa.actionOutput().port().value());
 		flowEntryObj.setOutPort(outport);
 	    }
 	}
+	if (! flowEntry.flowEntryActions().isEmpty()) {
+	    flowEntryObj.setActions(flowEntry.flowEntryActions().toString());
+	}
+
 	// TODO: Hacks with hard-coded state names!
 	if (found)
 	    flowEntryObj.setUserState("FE_USER_MODIFY");
@@ -1387,6 +1395,16 @@
 
 	    flowPath.setFlowEntryMatch(match);
 	}
+	//
+	// Extract the actions for the first Flow Entry
+	//
+	{
+	    String actionsStr = flowObj.getActions();
+	    if (actionsStr != null) {
+		FlowEntryActions flowEntryActions = new FlowEntryActions(actionsStr);
+		flowPath.setFlowEntryActions(flowEntryActions);
+	    }
+	}
 
 	//
 	// Extract all Flow Entries
@@ -1471,19 +1489,15 @@
 	//
 	// Extract the actions
 	//
-	ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
-	Short actionOutputPort = flowEntryObj.getActionOutput();
-	if (actionOutputPort != null) {
-	    FlowEntryAction action = new FlowEntryAction();
-	    action.setActionOutput(new Port(actionOutputPort));
-	    actions.add(action);
-	}
+	FlowEntryActions actions = new FlowEntryActions();
+	String actionsStr = flowEntryObj.getActions();
+	if (actionsStr != null)
+	    actions = new FlowEntryActions(actionsStr);
 	flowEntry.setFlowEntryActions(actions);
 	flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
 	flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
 	//
-	// TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
-	// and FlowEntryErrorState.
+	// TODO: Take care of FlowEntryErrorState.
 	//
 	return flowEntry;
     }
@@ -1518,6 +1532,7 @@
 	computedFlowPath.setFlowPathFlags(new FlowPathFlags(flowPath.flowPathFlags().flags()));
 	computedFlowPath.setDataPath(dataPath);
 	computedFlowPath.setFlowEntryMatch(new FlowEntryMatch(flowPath.flowEntryMatch()));
+	computedFlowPath.setFlowEntryActions(new FlowEntryActions(flowPath.flowEntryActions()));
 
 	FlowId flowId = new FlowId();
 	String dataPathSummaryStr = dataPath.dataPathSummary();
@@ -1543,21 +1558,35 @@
 	// Set the incoming port matching and the outgoing port output
 	// actions for each flow entry.
 	//
+	int idx = 0;
 	for (FlowEntry flowEntry : newDataPath.flowEntries()) {
 	    // Set the incoming port matching
 	    FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
 	    flowEntry.setFlowEntryMatch(flowEntryMatch);
 	    flowEntryMatch.enableInPort(flowEntry.inPort());
 
-	    // Set the outgoing port output action
-	    ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
-	    if (flowEntryActions == null) {
-		flowEntryActions = new ArrayList<FlowEntryAction>();
-		flowEntry.setFlowEntryActions(flowEntryActions);
+	    //
+	    // Set the actions
+	    //
+	    FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
+	    //
+	    // If the first Flow Entry, copy the Flow Path actions to it
+	    //
+	    if (idx == 0) {
+		String actionsStr = flowObj.getActions();
+		if (actionsStr != null) {
+		    FlowEntryActions flowActions = new FlowEntryActions(actionsStr);
+		    for (FlowEntryAction action : flowActions.actions())
+			flowEntryActions.addAction(action);
+		}
 	    }
+	    idx++;
+	    //
+	    // Add the outgoing port output action
+	    //
 	    FlowEntryAction flowEntryAction = new FlowEntryAction();
 	    flowEntryAction.setActionOutput(flowEntry.outPort());
-	    flowEntryActions.add(flowEntryAction);
+	    flowEntryActions.addAction(flowEntryAction);
 	}
 
 	//
@@ -1749,16 +1778,115 @@
 	//
 	// Fetch the actions
 	//
-	// TODO: For now we support only the "OUTPUT" actions.
-	//
-	List<OFAction> actions = new ArrayList<OFAction>();
-	Short actionOutputPort = flowEntryObj.getActionOutput();
-	if (actionOutputPort != null) {
-	    OFActionOutput action = new OFActionOutput();
-	    // XXX: The max length is hard-coded for now
-	    action.setMaxLength((short)0xffff);
-	    action.setPort(actionOutputPort);
-	    actions.add(action);
+	Short actionOutputPort = null;
+	List<OFAction> openFlowActions = new ArrayList<OFAction>();
+	int actionsLen = 0;
+	FlowEntryActions flowEntryActions = null;
+	String actionsStr = flowEntryObj.getActions();
+	if (actionsStr != null)
+	    flowEntryActions = new FlowEntryActions(actionsStr);
+	for (FlowEntryAction action : flowEntryActions.actions()) {
+	    ActionOutput actionOutput = action.actionOutput();
+	    ActionSetVlanId actionSetVlanId = action.actionSetVlanId();
+	    ActionSetVlanPriority actionSetVlanPriority = action.actionSetVlanPriority();
+	    ActionStripVlan actionStripVlan = action.actionStripVlan();
+	    ActionSetEthernetAddr actionSetEthernetSrcAddr = action.actionSetEthernetSrcAddr();
+	    ActionSetEthernetAddr actionSetEthernetDstAddr = action.actionSetEthernetDstAddr();
+	    ActionSetIPv4Addr actionSetIPv4SrcAddr = action.actionSetIPv4SrcAddr();
+	    ActionSetIPv4Addr actionSetIPv4DstAddr = action.actionSetIPv4DstAddr();
+	    ActionSetIpToS actionSetIpToS = action.actionSetIpToS();
+	    ActionSetTcpUdpPort actionSetTcpUdpSrcPort = action.actionSetTcpUdpSrcPort();
+	    ActionSetTcpUdpPort actionSetTcpUdpDstPort = action.actionSetTcpUdpDstPort();
+	    ActionEnqueue actionEnqueue = action.actionEnqueue();
+
+	    if (actionOutput != null) {
+		actionOutputPort = actionOutput.port().value();
+		// XXX: The max length is hard-coded for now
+		OFActionOutput ofa =
+		    new OFActionOutput(actionOutput.port().value(),
+				       (short)0xffff);
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetVlanId != null) {
+		OFActionVirtualLanIdentifier ofa =
+		    new OFActionVirtualLanIdentifier(actionSetVlanId.vlanId());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetVlanPriority != null) {
+		OFActionVirtualLanPriorityCodePoint ofa =
+		    new OFActionVirtualLanPriorityCodePoint(actionSetVlanPriority.vlanPriority());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionStripVlan != null) {
+		if (actionStripVlan.stripVlan() == true) {
+		    OFActionStripVirtualLan ofa = new OFActionStripVirtualLan();
+		    openFlowActions.add(ofa);
+		    actionsLen += ofa.getLength();
+		}
+	    }
+
+	    if (actionSetEthernetSrcAddr != null) {
+		OFActionDataLayerSource ofa = 
+		    new OFActionDataLayerSource(actionSetEthernetSrcAddr.addr().toBytes());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetEthernetDstAddr != null) {
+		OFActionDataLayerDestination ofa =
+		    new OFActionDataLayerDestination(actionSetEthernetDstAddr.addr().toBytes());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIPv4SrcAddr != null) {
+		OFActionNetworkLayerSource ofa =
+		    new OFActionNetworkLayerSource(actionSetIPv4SrcAddr.addr().value());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIPv4DstAddr != null) {
+		OFActionNetworkLayerDestination ofa =
+		    new OFActionNetworkLayerDestination(actionSetIPv4DstAddr.addr().value());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIpToS != null) {
+		OFActionNetworkTypeOfService ofa =
+		    new OFActionNetworkTypeOfService(actionSetIpToS.ipToS());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetTcpUdpSrcPort != null) {
+		OFActionTransportLayerSource ofa =
+		    new OFActionTransportLayerSource(actionSetTcpUdpSrcPort.port());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetTcpUdpDstPort != null) {
+		OFActionTransportLayerDestination ofa =
+		    new OFActionTransportLayerDestination(actionSetTcpUdpDstPort.port());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionEnqueue != null) {
+		OFActionEnqueue ofa =
+		    new OFActionEnqueue(actionEnqueue.port().value(),
+					actionEnqueue.queueId());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
 	}
 
 	fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
@@ -1768,8 +1896,8 @@
 	    .setCookie(cookie)
 	    .setCommand(flowModCommand)
 	    .setMatch(match)
-	    .setActions(actions)
-	    .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+	    .setActions(openFlowActions)
+	    .setLengthU(OFFlowMod.MINIMUM_LENGTH + actionsLen);
 	fm.setOutPort(OFPort.OFPP_NONE.getValue());
 	if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
 	    (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
@@ -1973,27 +2101,113 @@
 	//
 	// Fetch the actions
 	//
-	// TODO: For now we support only the "OUTPUT" actions.
+	Short actionOutputPort = null;
+	List<OFAction> openFlowActions = new ArrayList<OFAction>();
+	int actionsLen = 0;
+	FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
 	//
-	fm.setOutPort(OFPort.OFPP_NONE.getValue());
-	List<OFAction> actions = new ArrayList<OFAction>();
-	ArrayList<FlowEntryAction> flowEntryActions =
-	    flowEntry.flowEntryActions();
-	for (FlowEntryAction flowEntryAction : flowEntryActions) {
-	    FlowEntryAction.ActionOutput actionOutput =
-		flowEntryAction.actionOutput();
+	for (FlowEntryAction action : flowEntryActions.actions()) {
+	    ActionOutput actionOutput = action.actionOutput();
+	    ActionSetVlanId actionSetVlanId = action.actionSetVlanId();
+	    ActionSetVlanPriority actionSetVlanPriority = action.actionSetVlanPriority();
+	    ActionStripVlan actionStripVlan = action.actionStripVlan();
+	    ActionSetEthernetAddr actionSetEthernetSrcAddr = action.actionSetEthernetSrcAddr();
+	    ActionSetEthernetAddr actionSetEthernetDstAddr = action.actionSetEthernetDstAddr();
+	    ActionSetIPv4Addr actionSetIPv4SrcAddr = action.actionSetIPv4SrcAddr();
+	    ActionSetIPv4Addr actionSetIPv4DstAddr = action.actionSetIPv4DstAddr();
+	    ActionSetIpToS actionSetIpToS = action.actionSetIpToS();
+	    ActionSetTcpUdpPort actionSetTcpUdpSrcPort = action.actionSetTcpUdpSrcPort();
+	    ActionSetTcpUdpPort actionSetTcpUdpDstPort = action.actionSetTcpUdpDstPort();
+	    ActionEnqueue actionEnqueue = action.actionEnqueue();
+
 	    if (actionOutput != null) {
-		short actionOutputPort = actionOutput.port().value();
-		OFActionOutput action = new OFActionOutput();
+		actionOutputPort = actionOutput.port().value();
 		// XXX: The max length is hard-coded for now
-		action.setMaxLength((short)0xffff);
-		action.setPort(actionOutputPort);
-		actions.add(action);
-		if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
-		    (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
-		    fm.setOutPort(actionOutputPort);
+		OFActionOutput ofa =
+		    new OFActionOutput(actionOutput.port().value(),
+				       (short)0xffff);
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetVlanId != null) {
+		OFActionVirtualLanIdentifier ofa =
+		    new OFActionVirtualLanIdentifier(actionSetVlanId.vlanId());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetVlanPriority != null) {
+		OFActionVirtualLanPriorityCodePoint ofa =
+		    new OFActionVirtualLanPriorityCodePoint(actionSetVlanPriority.vlanPriority());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionStripVlan != null) {
+		if (actionStripVlan.stripVlan() == true) {
+		    OFActionStripVirtualLan ofa = new OFActionStripVirtualLan();
+		    openFlowActions.add(ofa);
+		    actionsLen += ofa.getLength();
 		}
 	    }
+
+	    if (actionSetEthernetSrcAddr != null) {
+		OFActionDataLayerSource ofa = 
+		    new OFActionDataLayerSource(actionSetEthernetSrcAddr.addr().toBytes());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetEthernetDstAddr != null) {
+		OFActionDataLayerDestination ofa =
+		    new OFActionDataLayerDestination(actionSetEthernetDstAddr.addr().toBytes());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIPv4SrcAddr != null) {
+		OFActionNetworkLayerSource ofa =
+		    new OFActionNetworkLayerSource(actionSetIPv4SrcAddr.addr().value());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIPv4DstAddr != null) {
+		OFActionNetworkLayerDestination ofa =
+		    new OFActionNetworkLayerDestination(actionSetIPv4DstAddr.addr().value());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetIpToS != null) {
+		OFActionNetworkTypeOfService ofa =
+		    new OFActionNetworkTypeOfService(actionSetIpToS.ipToS());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetTcpUdpSrcPort != null) {
+		OFActionTransportLayerSource ofa =
+		    new OFActionTransportLayerSource(actionSetTcpUdpSrcPort.port());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionSetTcpUdpDstPort != null) {
+		OFActionTransportLayerDestination ofa =
+		    new OFActionTransportLayerDestination(actionSetTcpUdpDstPort.port());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
+
+	    if (actionEnqueue != null) {
+		OFActionEnqueue ofa =
+		    new OFActionEnqueue(actionEnqueue.port().value(),
+					actionEnqueue.queueId());
+		openFlowActions.add(ofa);
+		actionsLen += ofa.getLength();
+	    }
 	}
 
 	fm.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
@@ -2003,8 +2217,14 @@
 	    .setCookie(cookie)
 	    .setCommand(flowModCommand)
 	    .setMatch(match)
-	    .setActions(actions)
-	    .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
+	    .setActions(openFlowActions)
+	    .setLengthU(OFFlowMod.MINIMUM_LENGTH + actionsLen);
+	fm.setOutPort(OFPort.OFPP_NONE.getValue());
+	if ((flowModCommand == OFFlowMod.OFPFC_DELETE) ||
+	    (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
+	    if (actionOutputPort != null)
+		fm.setOutPort(actionOutputPort);
+	}
 
 	//
 	// TODO: Set the following flag
@@ -2131,14 +2351,10 @@
 	    flowEntryMatch.enableInPort(flowEntry.inPort());
 
 	    // Set the outgoing port output action
-	    ArrayList<FlowEntryAction> flowEntryActions = flowEntry.flowEntryActions();
-	    if (flowEntryActions == null) {
-		flowEntryActions = new ArrayList<FlowEntryAction>();
-		flowEntry.setFlowEntryActions(flowEntryActions);
-	    }
+	    FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
 	    FlowEntryAction flowEntryAction = new FlowEntryAction();
 	    flowEntryAction.setActionOutput(flowEntry.outPort());
-	    flowEntryActions.add(flowEntryAction);
+	    flowEntryActions.addAction(flowEntryAction);
 	}
 
 	//
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/HostArpRequester.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/HostArpRequester.java
new file mode 100644
index 0000000..20c6a28
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/HostArpRequester.java
@@ -0,0 +1,28 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import net.floodlightcontroller.packet.ARP;
+
+public class HostArpRequester implements IArpRequester {
+
+	private IProxyArpService arpService;
+	private ARP arpRequest;
+	private long dpid;
+	private short port;
+	//private long requestTime; //in ms
+	
+	public HostArpRequester(IProxyArpService arpService, ARP arpRequest, 
+			long dpid, short port) {
+		
+		this.arpService = arpService;
+		this.arpRequest = arpRequest;
+		this.dpid = dpid;
+		this.port = port;
+		//this.requestTime = System.currentTimeMillis();
+	}
+
+	@Override
+	public void arpResponse(byte[] mac) {
+		arpService.sendArpReply(arpRequest, dpid, port, mac);
+	}
+
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java
new file mode 100644
index 0000000..2a74944
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java
@@ -0,0 +1,5 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+public interface IArpRequester {
+	public void arpResponse(byte[] mac);
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
new file mode 100644
index 0000000..4632aba
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
@@ -0,0 +1,41 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import java.net.InetAddress;
+
+import net.floodlightcontroller.packet.ARP;
+
+public interface IProxyArpService {
+	
+	public final int ARP_REQUEST_TIMEOUT = 2000; //ms
+	
+	/**
+	 * Tell the IProxyArpService to send an ARP reply with the targetMac to 
+	 * the host on the specified switchport.
+	 * @param arpRequest
+	 * @param dpid
+	 * @param port
+	 * @param targetMac
+	 */
+	public void sendArpReply(ARP arpRequest, long dpid, short port, byte[] targetMac);
+	
+	/**
+	 * Returns the mac address if there is a valid entry in the cache.
+	 * Otherwise returns null.
+	 * @param ipAddress
+	 * @return
+	 */
+	public byte[] getMacAddress(InetAddress ipAddress);
+	
+	/**
+	 * Tell the IProxyArpService to send an ARP request for the IP address.
+	 * The request will be broadcast out all edge ports in the network.
+	 * As an optimization, the IProxyArpService will first check its cache and
+	 * return the MAC address if it is already known. If not, the request will be
+	 * sent and the callback will be called when the MAC address is known
+	 * (or if the request times out). 
+	 * @param ipAddress
+	 * @param requester
+	 * @return
+	 */
+	public byte[] sendArpRequest(InetAddress ipAddress, IArpRequester requester);
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
index a5246c0..5c2a2b9 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -6,18 +6,23 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
 
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IOFMessageListener;
 import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.packet.ARP;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.topology.ITopologyService;
-import net.floodlightcontroller.topology.NodePortTuple;
 import net.floodlightcontroller.util.MACAddress;
 
 import org.openflow.protocol.OFMessage;
@@ -31,27 +36,98 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ProxyArpManager implements IOFMessageListener {
+public class ProxyArpManager implements IProxyArpService, IOFMessageListener {
 	private static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
 	
-	private final long ARP_ENTRY_TIMEOUT = 600000; //ms (== 5 mins)
+	private final long ARP_ENTRY_TIMEOUT = 600000; //ms (== 10 mins)
 	
+	private final long ARP_REQUEST_TIMEOUT_THREAD_PERIOD = 60000; //ms (== 1 min) 
+			
 	protected IFloodlightProviderService floodlightProvider;
 	protected ITopologyService topology;
+	protected IDeviceService devices;
 	
 	protected Map<InetAddress, ArpTableEntry> arpTable;
 	
+	//protected ConcurrentHashMap<InetAddress, Set<ArpRequest>> arpRequests;
+	protected ConcurrentHashMap<InetAddress, ArpRequest> arpRequests;
+	
+	private class ArpRequest {
+		private Set<IArpRequester> requesters;
+		private long requestTime;
+		
+		public ArpRequest(){
+			this.requesters = new HashSet<IArpRequester>();
+			this.requestTime = System.currentTimeMillis();
+		}
+		
+		public synchronized void addRequester(IArpRequester requester){
+			requestTime = System.currentTimeMillis();
+			requesters.add(requester);
+		}
+		
+		public boolean isExpired(){
+			return (System.currentTimeMillis() - requestTime) 
+					> IProxyArpService.ARP_REQUEST_TIMEOUT;
+		}
+		
+		public synchronized void dispatchReply(byte[] replyMacAddress){
+			for (IArpRequester requester : requesters){
+				requester.arpResponse(replyMacAddress);
+			}
+		}
+	}
+	
 	public ProxyArpManager(IFloodlightProviderService floodlightProvider,
-				ITopologyService topology){
+				ITopologyService topology, IDeviceService devices){
 		this.floodlightProvider = floodlightProvider;
 		this.topology = topology;
+		this.devices = devices;
 		
 		arpTable = new HashMap<InetAddress, ArpTableEntry>();
+		//arpRequests = new ConcurrentHashMap<InetAddress, Set<ArpRequest>>();
+		arpRequests = new ConcurrentHashMap<InetAddress, ArpRequest>();
+		
+		Timer arpRequestTimeoutTimer = new Timer();
+		arpRequestTimeoutTimer.scheduleAtFixedRate(new TimerTask() {
+			@Override
+			public void run() {
+				synchronized (arpRequests) {
+					log.debug("Current have {} outstanding requests", 
+							arpRequests.size());
+					
+					Iterator<Map.Entry<InetAddress, ArpRequest>> it 
+							= arpRequests.entrySet().iterator();
+					
+					while (it.hasNext()){
+						Map.Entry<InetAddress, ArpRequest> entry
+								= it.next();
+						
+						if (entry.getValue().isExpired()){
+							log.debug("Cleaning expired ARP request for {}", 
+									entry.getKey().getHostAddress());
+							it.remove();
+						}
+					}
+				}
+			}
+		}, 0, ARP_REQUEST_TIMEOUT_THREAD_PERIOD);
+	}
+	
+	private void storeRequester(InetAddress address, IArpRequester requester) {
+		synchronized (arpRequests) {
+			if (arpRequests.get(address) == null) {
+				arpRequests.put(address, new ArpRequest());
+			}
+			ArpRequest request = arpRequests.get(address);
+							
+			request.addRequester(requester);
+		}
 	}
 	
 	@Override
 	public String getName() {
-		return  "ProxyArpManager";
+		return "ProxyArpManager";
 	}
 
 	@Override
@@ -78,42 +154,95 @@
                 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
 		
 		if (eth.getEtherType() == Ethernet.TYPE_ARP){
-			
-			
 			ARP arp = (ARP) eth.getPayload();
 			
 			if (arp.getOpCode() == ARP.OP_REQUEST) {
-				log.debug("ARP request received for {}", bytesToStringAddr(arp.getTargetProtocolAddress()));
-				
-				byte[] mac = lookupArpTable(arp.getTargetProtocolAddress());
-				
-				if (mac == null){
-					//Mac address is not in our arp table. 
-					//We need to flood the request out edge ports
-					Set<NodePortTuple> broadcastPorts = topology.getBroadcastDomainPorts();
-					log.debug("size {}", broadcastPorts.size());
-					for (NodePortTuple nodePort : broadcastPorts){
-						log.debug("Port {}", nodePort);
-					}
-					broadcastArpRequestOutEdge(pi, sw.getId(), pi.getInPort());
-				}
-				else {
-					//We know the address, so send a reply
-					log.debug("Sending reply of {}", MACAddress.valueOf(mac).toString());
-					sendArpReply(arp, pi, mac, sw);
-				}
+				handleArpRequest(sw, pi, arp);
 			}
 			else if (arp.getOpCode() == ARP.OP_REPLY) {
-				log.debug("ARP reply recieved for {}", bytesToStringAddr(arp.getSenderProtocolAddress()));
-				
-				log.debug("arp table {}", arpTable.keySet());
-				
-				updateArpTable(arp);
+				handleArpReply(sw, pi, arp);
 			}
 		}
 		
+		//TODO should we propagate ARP or swallow it?
+		//Always propagate for now so DeviceManager can learn the host location
 		return Command.CONTINUE;
 	}
+	
+	protected void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp) {
+		log.debug("ARP request received for {}", 
+				bytesToStringAddr(arp.getTargetProtocolAddress()));
+		
+		byte[] mac = lookupArpTable(arp.getTargetProtocolAddress());
+		
+		if (mac == null){
+			//Mac address is not in our arp table.
+			
+			//TODO check what the DeviceManager thinks
+			
+			//Record where the request came from so we know where to send the reply
+			InetAddress target;
+			try {
+				 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
+			} catch (UnknownHostException e) {
+				log.debug("Invalid address in ARP request", e);
+				//return Command.CONTINUE; //Continue or stop?
+				return;
+			}
+			
+			storeRequester(target, new HostArpRequester(this, arp, sw.getId(), 
+					pi.getInPort()));
+			
+			//Flood the request out edge ports
+			broadcastArpRequestOutEdge(pi.getPacketData(), sw.getId(), pi.getInPort());
+		}
+		else {
+			//We know the address, so send a reply
+			log.debug("Sending reply of {}", MACAddress.valueOf(mac).toString());
+			//sendArpReply(arp, pi, mac, sw);
+			sendArpReply(arp, sw.getId(), pi.getInPort(), mac);
+		}
+	}
+	
+	protected void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
+		log.debug("ARP reply recieved for {}", 
+				bytesToStringAddr(arp.getSenderProtocolAddress()));
+		
+		updateArpTable(arp);
+		
+		//See if anyone's waiting for this ARP reply
+		InetAddress addr;
+		try {
+			addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
+		} catch (UnknownHostException e) {
+			return;
+		}
+		
+		ArpRequest request = null;
+		synchronized (arpRequests) {
+			request = arpRequests.get(addr);
+			if (request != null) {
+				arpRequests.remove(addr);
+			}
+		}
+		if (request != null && !request.isExpired()) {
+			request.dispatchReply(arp.getSenderHardwareAddress());
+		}
+		
+		/*
+		Set<ArpRequest> requests = arpRequests.get(addr);
+		if (requests != null){
+			
+			synchronized (requests) {
+				for (ArpRequest request : requests) {
+					if (!request.isExpired()){
+						request.getRequester().arpResponse(
+								arp.getSenderHardwareAddress());
+					}
+				}
+			}
+		}*/
+	}
 
 	private synchronized byte[] lookupArpTable(byte[] ipAddress){
 		InetAddress addr;
@@ -162,7 +291,35 @@
 		}
 	}
 	
-	private void broadcastArpRequestOutEdge(OFPacketIn pi, long inSwitch, short inPort){
+	private void sendArpRequestForAddress(InetAddress ipAddress) {
+		byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
+		byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+		byte[] broadcastMac = {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, 
+				(byte)0xff, (byte)0xff, (byte)0xff};
+		
+		ARP arpRequest = new ARP();
+		
+		arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
+			.setProtocolType(ARP.PROTO_TYPE_IP)
+			.setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
+			.setProtocolAddressLength((byte)4) //can't find the constant anywhere
+			.setOpCode(ARP.OP_REQUEST)
+			.setSenderHardwareAddress(zeroMac)
+			.setSenderProtocolAddress(zeroIpv4)
+			.setTargetHardwareAddress(zeroMac)
+			.setTargetProtocolAddress(ipAddress.getAddress());
+	
+		Ethernet eth = new Ethernet();
+		eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
+			.setSourceMACAddress(broadcastMac)
+			.setEtherType(Ethernet.TYPE_ARP)
+			.setPayload(arpRequest);
+		
+		broadcastArpRequestOutEdge(eth.serialize(), 0, OFPort.OFPP_NONE.getValue());
+	}
+	
+	//private void broadcastArpRequestOutEdge(OFPacketIn pi, long inSwitch, short inPort){
+	private void broadcastArpRequestOutEdge(byte[] arpRequest, long inSwitch, short inPort) {
 		for (IOFSwitch sw : floodlightProvider.getSwitches().values()){
 			Collection<Short> enabledPorts = sw.getEnabledPortNumbers();
 			Set<Short> linkPorts = topology.getPortsWithLinks(sw.getId());
@@ -176,7 +333,7 @@
 			OFPacketOut po = new OFPacketOut();
 			po.setInPort(OFPort.OFPP_NONE)
 				.setBufferId(-1)
-				.setPacketData(pi.getPacketData());
+				.setPacketData(arpRequest);
 				
 			List<OFAction> actions = new ArrayList<OFAction>();
 			
@@ -196,7 +353,7 @@
 			short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
 			po.setActionsLength(actionsLength);
 			po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength 
-					+ pi.getPacketData().length);
+					+ arpRequest.length);
 			
 			List<OFMessage> msgList = new ArrayList<OFMessage>();
 			msgList.add(po);
@@ -210,26 +367,30 @@
 		}
 	}
 	
-	private void sendArpReply(ARP arpRequest, OFPacketIn pi, byte[] macRequested, IOFSwitch sw){
+	public void sendArpReply(ARP arpRequest, long dpid, short port, byte[] targetMac) {
+	//private void sendArpReply(ARP arpRequest, OFPacketIn pi, byte[] macRequested, IOFSwitch sw){
 		ARP arpReply = new ARP();
 		arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
 			.setProtocolType(ARP.PROTO_TYPE_IP)
 			.setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
 			.setProtocolAddressLength((byte)4) //can't find the constant anywhere
 			.setOpCode(ARP.OP_REPLY)
-			.setSenderHardwareAddress(macRequested)
+			//.setSenderHardwareAddress(macRequested)
+			.setSenderHardwareAddress(targetMac)
 			.setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
 			.setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
 			.setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
 		
 		Ethernet eth = new Ethernet();
 		eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
-			.setSourceMACAddress(macRequested)
+			//.setSourceMACAddress(macRequested)
+			.setSourceMACAddress(targetMac)
 			.setEtherType(Ethernet.TYPE_ARP)
 			.setPayload(arpReply);
 		
 		List<OFAction> actions = new ArrayList<OFAction>();
-		actions.add(new OFActionOutput(pi.getInPort()));
+		//actions.add(new OFActionOutput(pi.getInPort()));
+		actions.add(new OFActionOutput(port));
 		
 		OFPacketOut po = new OFPacketOut();
 		po.setInPort(OFPort.OFPP_NONE)
@@ -242,9 +403,15 @@
 		
 		List<OFMessage> msgList = new ArrayList<OFMessage>();
 		msgList.add(po);
+
+		IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+		
+		if (sw == null) {
+			return;
+		}
 		
 		try {
-			log.debug("Sending ARP reply to {}/{}", HexString.toHexString(sw.getId()), pi.getInPort());
+			log.debug("Sending ARP reply to {}/{}", HexString.toHexString(sw.getId()), port);
 			sw.write(msgList, null);
 			sw.flush();
 		} catch (IOException e) {
@@ -254,7 +421,7 @@
 
 	//TODO this should be put somewhere more central. I use it in BgpRoute as well.
 	//We need a HexString.toHexString() equivalent.
-	private String bytesToStringAddr(byte[] bytes){
+	private String bytesToStringAddr(byte[] bytes) {
 		InetAddress addr;
 		try {
 			addr = InetAddress.getByAddress(bytes);
@@ -265,4 +432,22 @@
 		if (addr == null) return "";
 		else return addr.getHostAddress();
 	}
+	
+	
+	public byte[] getMacAddress(InetAddress ipAddress) {
+		return lookupArpTable(ipAddress.getAddress());
+	}
+	
+	public byte[] sendArpRequest(InetAddress ipAddress, IArpRequester requester) {
+		byte[] lookupMac;
+		if ((lookupMac = lookupArpTable(ipAddress.getAddress())) == null) {
+			return lookupMac;
+		}
+		
+		sendArpRequestForAddress(ipAddress);
+		
+		storeRequester(ipAddress, requester);
+		
+		return null;
+	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java b/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java
index 9b06743..dec70e3 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/DataPath.java
@@ -5,7 +5,7 @@
 import org.codehaus.jackson.annotate.JsonProperty;
 
 /**
- * The class representing the Data Path.
+ * The data forwarding path state from a source to a destination.
  */
 public class DataPath {
     private SwitchPort srcPort;		// The source port
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
index 7dd0699..049e783 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntry.java
@@ -12,10 +12,10 @@
  * support multiple in-ports and multiple out-ports.
  */
 public class FlowEntry {
-    private FlowId flowId;			// FlowID of flowEntry
+    private FlowId flowId;			// FlowID of the Flow Entry
     private FlowEntryId flowEntryId;		// The Flow Entry ID
     private FlowEntryMatch flowEntryMatch;	// The Flow Entry Match
-    private ArrayList<FlowEntryAction> flowEntryActions; // The Flow Entry Actions
+    private FlowEntryActions flowEntryActions;	// The Flow Entry Actions
     private Dpid dpid;				// The Switch DPID
     private Port inPort;		// The Switch incoming port. Used only
 					// when the entry is used to return
@@ -53,64 +53,64 @@
 	flowEntryMatch.enableDstTcpUdpPort((short)80);
 
 	FlowEntryAction action = null;
-	ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
+	FlowEntryActions actions = new FlowEntryActions();
 
 	action = new FlowEntryAction();
 	action.setActionOutput(new Port((short)12));
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionOutputToController((short)13);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetVlanId((short)14);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetVlanPriority((byte)15);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionStripVlan(true);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetEthernetSrcAddr(mac);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetEthernetDstAddr(mac);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetIPv4SrcAddr(ipv4);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetIPv4DstAddr(ipv4);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetIpToS((byte)16);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetTcpUdpSrcPort((short)17);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionSetTcpUdpDstPort((short)18);
-	actions.add(action);
+	actions.addAction(action);
 
 	action = new FlowEntryAction();
 	action.setActionEnqueue(new Port((short)19), 20);
-	actions.add(action);
+	actions.addAction(action);
 
 	setFlowEntryActions(actions);
 	*/
 
-
+	flowEntryActions = new FlowEntryActions();
 	flowEntryUserState = FlowEntryUserState.FE_USER_UNKNOWN;
 	flowEntrySwitchState = FlowEntrySwitchState.FE_SWITCH_UNKNOWN;
     }
@@ -173,7 +173,7 @@
      * @return the Flow Entry Actions.
      */
     @JsonProperty("flowEntryActions")
-    public ArrayList<FlowEntryAction> flowEntryActions() {
+    public FlowEntryActions flowEntryActions() {
 	return flowEntryActions;
     }
 
@@ -183,7 +183,7 @@
      * @param flowEntryActions the Flow Entry Actions to set.
      */
     @JsonProperty("flowEntryActions")
-    public void setFlowEntryActions(ArrayList<FlowEntryAction> flowEntryActions) {
+    public void setFlowEntryActions(FlowEntryActions flowEntryActions) {
 	this.flowEntryActions = flowEntryActions;
     }
 
@@ -319,8 +319,7 @@
      * Convert the flow entry to a string.
      *
      * The string has the following form:
-     *  [flowEntryId=XXX flowEntryMatch=XXX flowEntryAction=XXX
-     *   flowEntryAction=XXX flowEntryAction=XXX dpid=XXX
+     *  [flowEntryId=XXX flowEntryMatch=XXX flowEntryActions=XXX dpid=XXX
      *   inPort=XXX outPort=XXX flowEntryUserState=XXX flowEntrySwitchState=XXX
      *   flowEntryErrorState=XXX]
      * @return the flow entry as a string.
@@ -329,9 +328,7 @@
     public String toString() {
 	String ret = "[flowEntryId=" + this.flowEntryId.toString();
 	ret += " flowEntryMatch=" + this.flowEntryMatch.toString();
-	for (FlowEntryAction fa : flowEntryActions) {
-	    ret += " flowEntryAction=" + fa.toString();
-	}
+	ret += " flowEntryActions=" + this.flowEntryActions.toString();
 	ret += " dpid=" + this.dpid.toString();
 	ret += " inPort=" + this.inPort.toString();
 	ret += " outPort=" + this.outPort.toString();
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryAction.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryAction.java
index 22aef98..f150983 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryAction.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryAction.java
@@ -7,7 +7,9 @@
 /**
  * The class representing a single Flow Entry action.
  *
- * A set of Flow Entry actions need to be applied to each packet.
+ * A Flow Entry action that needs to be applied to each packet.
+ * Note that it contains only a single action. Multiple actions are
+ * listed in a list inside @ref FlowEntryActions.
  */
 public class FlowEntryAction {
     /**
@@ -59,6 +61,28 @@
 	    this.maxLen = 0;
 	}
 
+	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionOutput(ActionOutput other) {
+	    if (other.port != null)
+		this.port = new Port(other.port);
+	    this.maxLen = other.maxLen;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX maxLen=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionOutput(String actionStr) {
+	    this.fromString(actionStr);
+	}
 
 	/**
 	 * Constructor for a given output port and maximum length.
@@ -121,6 +145,54 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX maxLen=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split(" ");
+	    String decode = null;
+
+	    // Decode the "port=XXX" part
+	    if (parts.length > 0)
+		decode = parts[0];
+	    if (decode != null) {
+		String[] tokens = decode.split("port=");
+		if (tokens.length > 1 && tokens[1] != null) {
+		    try {
+			Short valueShort = Short.valueOf(tokens[1]);
+			port = new Port(valueShort);
+		    } catch (NumberFormatException e) {
+			throw new IllegalArgumentException("Invalid action string");
+		    }
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+
+	    // Decode the "maxLen=XXX" part
+	    decode = null;
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		String[] tokens = decode.split("maxLen=");
+		if (tokens.length > 1 && tokens[1] != null) {
+		    try {
+			maxLen = Short.valueOf(tokens[1]);
+		    } catch (NumberFormatException e) {
+			throw new IllegalArgumentException("Invalid action string");
+		    }
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -137,6 +209,27 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetVlanId(ActionSetVlanId other) {
+	    this.vlanId = other.vlanId;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [vlanId=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetVlanId(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given VLAN ID.
 	 *
 	 * @param vlanId the VLAN ID to set.
@@ -171,6 +264,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [vlanId=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("vlanId=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    vlanId = Short.valueOf(decode);
+		} catch (NumberFormatException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -187,6 +307,27 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetVlanPriority(ActionSetVlanPriority other) {
+	    this.vlanPriority = other.vlanPriority;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [vlanPriority=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetVlanPriority(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given VLAN priority.
 	 *
 	 * @param vlanPriority the VLAN priority to set.
@@ -221,6 +362,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [vlanPriority=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("vlanPriority=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    vlanPriority = Byte.valueOf(decode);
+		} catch (NumberFormatException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -237,6 +405,27 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionStripVlan(ActionStripVlan other) {
+	    this.stripVlan = other.stripVlan;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [stripVlan=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionStripVlan(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given boolean flag.
 	 *
 	 * @param stripVlan if true, strip the VLAN header.
@@ -271,6 +460,29 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [stripVlan=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("stripVlan=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		stripVlan = Boolean.valueOf(decode);
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -288,6 +500,28 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetEthernetAddr(ActionSetEthernetAddr other) {
+	    if (other.addr != null)
+		this.addr = MACAddress.valueOf(other.addr.toLong());
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [addr=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetEthernetAddr(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given MAC address.
 	 *
 	 * @param addr the MAC address to set.
@@ -322,6 +556,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [addr=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("addr=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    addr = MACAddress.valueOf(decode);
+		} catch (IllegalArgumentException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -339,6 +600,28 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetIPv4Addr(ActionSetIPv4Addr other) {
+	    if (other.addr != null)
+		this.addr = new IPv4(other.addr);
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [addr=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetIPv4Addr(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given IPv4 address.
 	 *
 	 * @param addr the IPv4 address to set.
@@ -373,6 +656,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [addr=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("addr=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    addr = new IPv4(decode);
+		} catch (IllegalArgumentException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -390,6 +700,27 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetIpToS(ActionSetIpToS other) {
+	    this.ipToS = other.ipToS;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [ipToS=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetIpToS(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given IP ToS (DSCP field, 6 bits).
 	 *
 	 * @param ipToS the IP ToS (DSCP field, 6 bits) to set.
@@ -424,6 +755,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [ipToS=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("ipToS=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    ipToS = Byte.valueOf(decode);
+		} catch (NumberFormatException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -441,6 +799,27 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionSetTcpUdpPort(ActionSetTcpUdpPort other) {
+	    this.port = other.port;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionSetTcpUdpPort(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given TCP/UDP port.
 	 *
 	 * @param port the TCP/UDP port to set.
@@ -475,6 +854,33 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split("port=");
+	    String decode = null;
+
+	    // Decode the value
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		try {
+		    port = Short.valueOf(decode);
+		} catch (NumberFormatException e) {
+		    throw new IllegalArgumentException("Invalid action string");
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     /**
@@ -495,6 +901,29 @@
 	}
 
 	/**
+	 * Copy constructor.
+	 *
+	 * @param other the object to copy from.
+	 */
+	public ActionEnqueue(ActionEnqueue other) {
+	    if (other.port != null)
+		this.port = new Port(other.port);
+	    this.queueId = other.queueId;
+	}
+
+	/**
+	 * Constructor from a string.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX queueId=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public ActionEnqueue(String actionStr) {
+	    this.fromString(actionStr);
+	}
+
+	/**
 	 * Constructor for a given port and queue ID.
 	 *
 	 * @param port the port to set.
@@ -542,6 +971,54 @@
 
 	    return ret;
 	}
+
+	/**
+	 * Convert a string to an action.
+	 *
+	 * The string has the following form:
+	 *  [port=XXX queueId=XXX]
+	 *
+	 * @param actionStr the action as a string.
+	 */
+	public void fromString(String actionStr) {
+	    String[] parts = actionStr.split(" ");
+	    String decode = null;
+
+	    // Decode the "port=XXX" part
+	    if (parts.length > 0)
+		decode = parts[0];
+	    if (decode != null) {
+		String[] tokens = decode.split("port=");
+		if (tokens.length > 1 && tokens[1] != null) {
+		    try {
+			Short valueShort = Short.valueOf(tokens[1]);
+			port = new Port(valueShort);
+		    } catch (NumberFormatException e) {
+			throw new IllegalArgumentException("Invalid action string");
+		    }
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+
+	    // Decode the "queueId=XXX" part
+	    decode = null;
+	    if (parts.length > 1)
+		decode = parts[1];
+	    if (decode != null) {
+		decode = decode.replace("]", "");
+		String[] tokens = decode.split("queueId=");
+		if (tokens.length > 1 && tokens[1] != null) {
+		    try {
+			queueId = Short.valueOf(tokens[1]);
+		    } catch (NumberFormatException e) {
+			throw new IllegalArgumentException("Invalid action string");
+		    }
+		}
+	    } else {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	}
     }
 
     private ActionValues actionType;	// The action type
@@ -571,6 +1048,88 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public FlowEntryAction(FlowEntryAction other) {
+	this.actionType = other.actionType;
+
+	//
+	if (other.actionOutput != null)
+	    this.actionOutput = new ActionOutput(other.actionOutput);
+	else
+	    this.actionOutput = null;
+	//
+	if (other.actionSetVlanId != null)
+	    this.actionSetVlanId = new ActionSetVlanId(other.actionSetVlanId);
+	else
+	    this.actionSetVlanId = null;
+	//
+	if (other.actionSetVlanPriority != null)
+	    this.actionSetVlanPriority = new ActionSetVlanPriority(other.actionSetVlanPriority);
+	else
+	    this.actionSetVlanPriority = null;
+	//
+	if (other.actionStripVlan != null)
+	    this.actionStripVlan = new ActionStripVlan(other.actionStripVlan);
+	else
+	    this.actionStripVlan = null;
+	//
+	if (other.actionSetEthernetSrcAddr != null)
+	    this.actionSetEthernetSrcAddr = new ActionSetEthernetAddr(other.actionSetEthernetSrcAddr);
+	else
+	    this.actionSetEthernetSrcAddr = null;
+	//
+	if (other.actionSetEthernetDstAddr != null)
+	    this.actionSetEthernetDstAddr = new ActionSetEthernetAddr(other.actionSetEthernetDstAddr);
+	else
+	    this.actionSetEthernetDstAddr = null;
+	//
+	if (other.actionSetIPv4SrcAddr != null)
+	    this.actionSetIPv4SrcAddr = new ActionSetIPv4Addr(other.actionSetIPv4SrcAddr);
+	else
+	    this.actionSetIPv4SrcAddr = null;
+	//
+	if (other.actionSetIPv4DstAddr != null)
+	    this.actionSetIPv4DstAddr = new ActionSetIPv4Addr(other.actionSetIPv4DstAddr);
+	else
+	    this.actionSetIPv4DstAddr = null;
+	//
+	if (other.actionSetIpToS != null)
+	    this.actionSetIpToS = new ActionSetIpToS(other.actionSetIpToS);
+	else
+	    this.actionSetIpToS = null;
+	//
+	if (other.actionSetTcpUdpSrcPort != null)
+	    this.actionSetTcpUdpSrcPort = new ActionSetTcpUdpPort(other.actionSetTcpUdpSrcPort);
+	else
+	    this.actionSetTcpUdpSrcPort = null;
+	//
+	if (other.actionSetTcpUdpDstPort != null)
+	    this.actionSetTcpUdpDstPort = new ActionSetTcpUdpPort(other.actionSetTcpUdpDstPort);
+	else
+	    this.actionSetTcpUdpDstPort = null;
+	//
+	if (other.actionEnqueue != null)
+	    this.actionEnqueue = new ActionEnqueue(other.actionEnqueue);
+	else
+	    this.actionEnqueue = null;
+    }
+
+    /**
+     * Constructor from a string.
+     *
+     * The string has the following form:
+     *  [type=XXX action=XXX]
+     *
+     * @param actionStr the action as a string.
+     */
+    public FlowEntryAction(String actionStr) {
+	this.fromString(actionStr);
+    }
+
+    /**
      * Get the action type.
      *
      * @return the action type.
@@ -957,12 +1516,12 @@
     }
 
     /**
-     * Convert the set of actions to a string.
+     * Convert the action to a string.
      *
      * The string has the following form:
      *  [type=XXX action=XXX]
      *
-     * @return the set of actions as a string.
+     * @return the action as a string.
      */
     @Override
     public String toString() {
@@ -1010,4 +1569,96 @@
 
 	return ret;
     }
+
+    /**
+     * Convert a string to an action.
+     *
+     * The string has the following form:
+     *  [type=XXX action=XXX]
+     *
+     * @param actionStr the action as a string.
+     */
+    public void fromString(String actionStr) {
+	String[] parts = actionStr.split("type=");
+	String decode = null;
+
+	// Extract the string after the "type="
+	if (parts.length > 1)
+	    decode = parts[1];
+	if (decode == null)
+	    throw new IllegalArgumentException("Invalid action string");
+
+	// Remove the trailing ']'
+	if ((decode.length() > 0) && (decode.charAt(decode.length() - 1) == ']')) {
+	    decode = decode.substring(0, decode.length() - 1);
+	} else {
+	    throw new IllegalArgumentException("Invalid action string");
+	}
+
+	// Extract the type value and the action value
+	parts = decode.split(" action=");
+
+	// Decode the "type=XXX" payload
+	if (parts.length > 0)
+	    decode = parts[0];
+	if (decode != null) {
+	    try {
+		actionType = Enum.valueOf(ActionValues.class, decode);
+	    } catch (IllegalArgumentException e) {
+		throw new IllegalArgumentException("Invalid action string");
+	    }
+	} else {
+	    throw new IllegalArgumentException("Invalid action string");
+	}
+
+	// Decode the "action=XXX" payload
+	decode = null;
+	if (parts.length > 1)
+	    decode = parts[1];
+	if (decode == null)
+	    throw new IllegalArgumentException("Invalid action string");
+	//
+	try {
+	    switch (actionType) {
+	    case ACTION_OUTPUT:
+		actionOutput = new ActionOutput(decode);
+		break;
+	    case ACTION_SET_VLAN_VID:
+		actionSetVlanId = new ActionSetVlanId(decode);
+		break;
+	    case ACTION_SET_VLAN_PCP:
+		actionSetVlanPriority = new ActionSetVlanPriority(decode);
+		break;
+	    case ACTION_STRIP_VLAN:
+		actionStripVlan = new ActionStripVlan(decode);
+		break;
+	    case ACTION_SET_DL_SRC:
+		actionSetEthernetSrcAddr = new ActionSetEthernetAddr(decode);
+		break;
+	    case ACTION_SET_DL_DST:
+		actionSetEthernetDstAddr = new ActionSetEthernetAddr(decode);
+		break;
+	    case ACTION_SET_NW_SRC:
+		actionSetIPv4SrcAddr = new ActionSetIPv4Addr(decode);
+		break;
+	    case ACTION_SET_NW_DST:
+		actionSetIPv4DstAddr = new ActionSetIPv4Addr(decode);
+		break;
+	    case ACTION_SET_NW_TOS:
+		actionSetIpToS = new ActionSetIpToS(decode);
+		break;
+	    case ACTION_SET_TP_SRC:
+		actionSetTcpUdpSrcPort = new ActionSetTcpUdpPort(decode);
+		break;
+	    case ACTION_SET_TP_DST:
+		actionSetTcpUdpDstPort = new ActionSetTcpUdpPort(decode);
+		break;
+	    case ACTION_ENQUEUE:
+		actionEnqueue = new ActionEnqueue(decode);
+		break;
+	    }
+	} catch (IllegalArgumentException e) {
+	    throw new IllegalArgumentException("Invalid action string");
+	}
+    }
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryActions.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryActions.java
new file mode 100644
index 0000000..46f638d
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowEntryActions.java
@@ -0,0 +1,146 @@
+package net.onrc.onos.ofcontroller.util;
+
+import java.util.ArrayList;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing multiple Flow Entry actions.
+ *
+ * A set of Flow Entry actions need to be applied to each packet.
+ */
+public class FlowEntryActions {
+    private ArrayList<FlowEntryAction> actions;	// The Flow Entry Actions
+
+    /**
+     * Default constructor.
+     */
+    public FlowEntryActions() {
+	actions = new ArrayList<FlowEntryAction>();
+    }
+
+    /**
+     * Constructor from a string.
+     *
+     * The string has the following form:
+     *  [[type=XXX action=XXX];[type=XXX action=XXX];...;]
+     *
+     * @param actionsStr the set of actions as a string.
+     */
+    public FlowEntryActions(String actionsStr) {
+	this.fromString(actionsStr);
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public FlowEntryActions(FlowEntryActions other) {
+	actions = new ArrayList<FlowEntryAction>();
+
+	for (FlowEntryAction action : other.actions) {
+	    FlowEntryAction newAction = new FlowEntryAction(action);
+	    actions.add(newAction);
+	}
+    }
+
+    /**
+     * Get the Flow Entry Actions.
+     *
+     * @return the Flow Entry Actions.
+     */
+    @JsonProperty("actions")
+    public ArrayList<FlowEntryAction> actions() {
+	return actions;
+    }
+
+    /**
+     * Set the Flow Entry Actions.
+     *
+     * @param actions the Flow Entry Actions to set.
+     */
+    @JsonProperty("actions")
+    public void setActions(ArrayList<FlowEntryAction> actions) {
+	this.actions = actions;
+    }
+
+    /**
+     * Add a Flow Entry Action.
+     *
+     * @param FlowEntryAction the Flow Entry Action to add.
+     */
+    public void addAction(FlowEntryAction flowEntryAction) {
+	actions.add(flowEntryAction);
+    }
+
+    /**
+     * Test whether the set of actions is empty.
+     *
+     * @return true if the set of actions is empty, otherwise false.
+     */
+    public Boolean isEmpty() {
+	return actions.isEmpty();
+    }
+
+    /**
+     * Convert the set of actions to a string.
+     *
+     * The string has the following form:
+     *  [[type=XXX action=XXX];[type=XXX action=XXX];...;]
+     *
+     * @return the set of actions as a string.
+     */
+    @Override
+    public String toString() {
+	String ret = "[";
+	for (FlowEntryAction action : actions) {
+	    ret += action.toString() + ";";
+	}
+	ret += "]";
+
+	return ret;
+    }
+
+    /**
+     * Convert a string to a set of actions.
+     *
+     * The string has the following form:
+     *  [[type=XXX action=XXX];[type=XXX action=XXX];...;]
+     *
+     * @param actionsStr the set of actions as a string.
+     */
+    public void fromString(String actionsStr) {
+	String decode = actionsStr;
+
+	actions = new ArrayList<FlowEntryAction>();
+
+	if (decode.isEmpty())
+	    return;		// Nothing to do
+
+	// Remove the '[' and ']' in the beginning and the end of the string
+	if ((decode.length() > 1) && (decode.charAt(0) == '[') &&
+	    (decode.charAt(decode.length() - 1) == ']')) {
+	    decode = decode.substring(1, decode.length() - 1);
+	} else {
+	    throw new IllegalArgumentException("Invalid action string");
+	}
+
+	// Split the string, and decode each action
+	String[] parts = decode.split(";");
+	for (int i = 0; i < parts.length; i++) {
+	    decode = parts[i];
+	    if ((decode == null) || decode.isEmpty())
+		continue;
+	    FlowEntryAction flowEntryAction = null;
+	    try {
+		flowEntryAction = new FlowEntryAction(decode);
+	    } catch (IllegalArgumentException e) {
+		// TODO: Ignore invalid actions for now
+		continue;
+	    }
+	    if (flowEntryAction != null)
+		actions.add(flowEntryAction);
+	}
+    }
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
index 2f4d421..153e184 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/FlowPath.java
@@ -19,6 +19,8 @@
     private DataPath dataPath;		// The data path
     private FlowEntryMatch flowEntryMatch; // Common Flow Entry Match for all
 					// Flow Entries
+    private FlowEntryActions flowEntryActions; // The Flow Entry Actions for
+					// the first Flow Entry
 
     /**
      * Default constructor.
@@ -26,6 +28,7 @@
     public FlowPath() {
 	flowPathFlags = new FlowPathFlags();
 	dataPath = new DataPath();
+	flowEntryActions = new FlowEntryActions();
     }
 
     /**
@@ -82,6 +85,19 @@
     	    this.setFlowEntryMatch(match);
 	}
 
+	//
+	// Extract the actions for the first Flow Entry
+	//
+	{
+	    FlowEntryActions actions = new FlowEntryActions();
+
+	    String actionsStr = flowObj.getActions();
+	    if (actions != null)
+		actions = new FlowEntryActions(actionsStr);
+
+	    this.setFlowEntryActions(actions);
+	}
+
     	//
     	// Extract all Flow Entries
     	//
@@ -95,64 +111,77 @@
     	    // Extract the match conditions
     	    //
     	    FlowEntryMatch match = new FlowEntryMatch();
+	    //
     	    Short matchInPort = flowEntryObj.getMatchInPort();
     	    if (matchInPort != null)
     		match.enableInPort(new Port(matchInPort));
+	    //
     	    String matchSrcMac = flowEntryObj.getMatchSrcMac();
     	    if (matchSrcMac != null)
     		match.enableSrcMac(MACAddress.valueOf(matchSrcMac));
+	    //
     	    String matchDstMac = flowEntryObj.getMatchDstMac();
     	    if (matchDstMac != null)
     		match.enableDstMac(MACAddress.valueOf(matchDstMac));
+	    //
     	    Short matchEthernetFrameType = flowEntryObj.getMatchEthernetFrameType();
     	    if (matchEthernetFrameType != null)
     		match.enableEthernetFrameType(matchEthernetFrameType);
+	    //
     	    Short matchVlanId = flowEntryObj.getMatchVlanId();
     	    if (matchVlanId != null)
     		match.enableVlanId(matchVlanId);
+	    //
     	    Byte matchVlanPriority = flowEntryObj.getMatchVlanPriority();
     	    if (matchVlanPriority != null)
     		match.enableVlanPriority(matchVlanPriority);
+	    //
     	    String matchSrcIPv4Net = flowEntryObj.getMatchSrcIPv4Net();
     	    if (matchSrcIPv4Net != null)
     		match.enableSrcIPv4Net(new IPv4Net(matchSrcIPv4Net));
+	    //
     	    String matchDstIPv4Net = flowEntryObj.getMatchDstIPv4Net();
     	    if (matchDstIPv4Net != null)
     		match.enableDstIPv4Net(new IPv4Net(matchDstIPv4Net));
+	    //
     	    Byte matchIpProto = flowEntryObj.getMatchIpProto();
     	    if (matchIpProto != null)
     		match.enableIpProto(matchIpProto);
+	    //
     	    Byte matchIpToS = flowEntryObj.getMatchIpToS();
     	    if (matchIpToS != null)
     		match.enableIpToS(matchIpToS);
+	    //
     	    Short matchSrcTcpUdpPort = flowEntryObj.getMatchSrcTcpUdpPort();
     	    if (matchSrcTcpUdpPort != null)
     		match.enableSrcTcpUdpPort(matchSrcTcpUdpPort);
+	    //
     	    Short matchDstTcpUdpPort = flowEntryObj.getMatchDstTcpUdpPort();
     	    if (matchDstTcpUdpPort != null)
     		match.enableDstTcpUdpPort(matchDstTcpUdpPort);
+	    //
     	    flowEntry.setFlowEntryMatch(match);
 
-    	    //
-    	    // Extract the actions
-    	    //
-    	    ArrayList<FlowEntryAction> actions = new ArrayList<FlowEntryAction>();
-    	    Short actionOutputPort = flowEntryObj.getActionOutput();
-    	    if (actionOutputPort != null) {
-    		FlowEntryAction action = new FlowEntryAction();
-    		action.setActionOutput(new Port(actionOutputPort));
-    		actions.add(action);
-    	    }
-    	    flowEntry.setFlowEntryActions(actions);
+	    //
+	    // Extract the actions
+	    //
+	    {
+		FlowEntryActions actions = new FlowEntryActions();
+
+		String actionsStr = flowObj.getActions();
+		if (actions != null)
+		    actions = new FlowEntryActions(actionsStr);
+
+		flowEntry.setFlowEntryActions(actions);
+	    }
 
     	    String userState = flowEntryObj.getUserState();
     	    flowEntry.setFlowEntryUserState(FlowEntryUserState.valueOf(userState));
     	    String switchState = flowEntryObj.getSwitchState();
     	    flowEntry.setFlowEntrySwitchState(FlowEntrySwitchState.valueOf(switchState));
-    	    //
-    	    // TODO: Take care of the FlowEntryMatch, FlowEntryAction set,
-    	    // and FlowEntryErrorState.
-    	    //
+	    //
+	    // TODO: Take care of the FlowEntryErrorState.
+	    //
     	    this.dataPath().flowEntries().add(flowEntry);
     	}
     }
@@ -249,10 +278,32 @@
     }
 
     /**
+     * Get the flow path's flow entry actions for the first Flow Entry.
+     *
+     * @return the flow path's flow entry actions for the first Flow Entry.
+     */
+    @JsonProperty("flowEntryActions")
+    public FlowEntryActions flowEntryActions() {
+	return flowEntryActions;
+    }
+
+    /**
+     * Set the flow path's flow entry actions for the first Flow Entry.
+     *
+     * @param flowEntryActions the flow path's flow entry actions for the first
+     * Flow Entry.
+     */
+    @JsonProperty("flowEntryActions")
+    public void setFlowEntryActions(FlowEntryActions flowEntryActions) {
+	this.flowEntryActions = flowEntryActions;
+    }
+
+    /**
      * Convert the flow path to a string.
      *
      * The string has the following form:
-     *  [flowId=XXX installerId=XXX flowPathFlags=XXX dataPath=XXX]
+     *  [flowId=XXX installerId=XXX flowPathFlags=XXX dataPath=XXX
+     *   flowEntryMatch=XXX flowEntryActions=XXX]
      *
      * @return the flow path as a string.
      */
@@ -265,6 +316,8 @@
 	    ret += " dataPath=" + this.dataPath.toString();
 	if (flowEntryMatch != null)
 	    ret += " flowEntryMatch=" + this.flowEntryMatch.toString();
+	if (flowEntryActions != null)
+	    ret += " flowEntryActions=" + this.flowEntryActions.toString();
 	ret += "]";
 	return ret;
     }
@@ -276,5 +329,4 @@
     public int compareTo(FlowPath f) {
     	return (int) (this.flowId.value() - f.flowId.value());
     }
-
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/IPv4.java b/src/main/java/net/onrc/onos/ofcontroller/util/IPv4.java
index 2081bbe..119165c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/IPv4.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/IPv4.java
@@ -22,6 +22,15 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public IPv4(IPv4 other) {
+	this.value = other.value;
+    }
+
+    /**
      * Constructor from an integer value.
      *
      * @param value the value to use.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/IPv4Net.java b/src/main/java/net/onrc/onos/ofcontroller/util/IPv4Net.java
index 52e6535..9ce247c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/IPv4Net.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/IPv4Net.java
@@ -23,6 +23,17 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public IPv4Net(IPv4Net other) {
+	if (other.address != null)
+	    this.address = new IPv4(other.address);
+	this.prefixLen = other.prefixLen;
+    }
+
+    /**
      * Constructor for a given address and prefix length.
      *
      * @param address the address to use.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/IPv6.java b/src/main/java/net/onrc/onos/ofcontroller/util/IPv6.java
index 2d28db8..f5dae11 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/IPv6.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/IPv6.java
@@ -25,6 +25,16 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public IPv6(IPv6 other) {
+	this.valueHigh = other.valueHigh;
+	this.valueLow = other.valueLow;
+    }
+
+    /**
      * Constructor from integer values.
      *
      * @param valueHigh the higher (more significant) 64 bits of the address.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/IPv6Net.java b/src/main/java/net/onrc/onos/ofcontroller/util/IPv6Net.java
index bafff24..65ffe54 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/IPv6Net.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/IPv6Net.java
@@ -23,6 +23,17 @@
     }
 
     /**
+     * Copy constructor.
+     *
+     * @param other the object to copy from.
+     */
+    public IPv6Net(IPv6Net other) {
+	if (other.address != null)
+	    this.address = new IPv6(other.address);
+	this.prefixLen = other.prefixLen;
+    }
+
+    /**
      * Constructor for a given address and prefix length.
      *
      * @param address the address to use.
diff --git a/src/main/java/net/onrc/onos/ofcontroller/util/Port.java b/src/main/java/net/onrc/onos/ofcontroller/util/Port.java
index 34c807d..bb4851c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/util/Port.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/util/Port.java
@@ -78,9 +78,9 @@
     }
 
     /**
-     * Constructor from another entry.
+     * Copy constructor.
      *
-     * @param other the other entry to use.
+     * @param other the object to copy from.
      */
     public Port(Port other) {
 	this.value = other.value();
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowEntryTest.java b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowEntryTest.java
index 717d688..06d8522 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowEntryTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/INetMapTopologyObjectsIFlowEntryTest.java
@@ -353,18 +353,18 @@
 	
 	/**
 	 * Desc:
-	 *  Test method for set and get ActionOutput.
+	 *  Test method for set and get ActionOutputPort.
 	 * Condition:
 	 *  N/A
 	 * Expect:
-	 * 1. Should set ActionOutput.
-	 * 2. Should get ActionOutput.
+	 * 1. Should set ActionOutputPort.
+	 * 2. Should get ActionOutputPort.
 	 */
 	@Test
-	public void testSetGetActionOutput() {
-		Short actionOutput = 1;
-		flowEntry.setActionOutput(actionOutput);
-		assertEquals(flowEntry.getActionOutput(), actionOutput);
+	public void testSetGetActionOutputPort() {
+		Short actionOutputPort = 1;
+		flowEntry.setActionOutputPort(actionOutputPort);
+		assertEquals(flowEntry.getActionOutputPort(), actionOutputPort);
 	}
 	
 	/**
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
index f746060..bcff36a 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/internal/TestableGraphDBOperation.java
@@ -425,6 +425,7 @@
 		private String matchSrcIpaddr,matchDstIpaddr;
 		private Byte matchIpProto, matchIpToS;
 		private Short matchSrcTcpUdpPort, matchDstTcpUdpPort;
+		private String actions;
 		
 		private List<IFlowEntry> entries;
 		private List<ISwitchObject> switches;
@@ -440,6 +441,7 @@
 		private String matchSrcIpaddrToUpdate,matchDstIpaddrToUpdate;
 		private Byte matchIpProtoToUpdate, matchIpToSToUpdate;
 		private Short matchSrcTcpUdpPortToUpdate, matchDstTcpUdpPortToUpdate;
+		private String actionsToUpdate;
 
 		private List<IFlowEntry> flowsToAdd;
 		private List<IFlowEntry> flowsToRemove;
@@ -485,6 +487,7 @@
 			if(matchIpToSToUpdate != null) { matchIpToS = matchIpToSToUpdate; }
 			if(matchSrcTcpUdpPortToUpdate != null) { matchSrcTcpUdpPort = matchSrcTcpUdpPortToUpdate; }
 			if(matchDstTcpUdpPortToUpdate != null) { matchDstTcpUdpPort = matchDstTcpUdpPortToUpdate; }
+			if(actionsToUpdate != null) { actions = actionsToUpdate; }
 		}
 		
 		public void rollback() {
@@ -506,6 +509,7 @@
 			matchSrcIpaddrToUpdate = matchDstIpaddrToUpdate = null;
 			matchIpProtoToUpdate = matchIpToSToUpdate = null;
 			matchSrcTcpUdpPortToUpdate = matchDstTcpUdpPortToUpdate = null;
+			actionsToUpdate = null;
 		}
 		
 		// Setter methods for test
@@ -531,6 +535,7 @@
 		public void setMatchIpToSForTest(Byte matchIpToS) { this.matchIpToS = matchIpToS; }
 		public void setMatchSrcTcpUdpPortForTest(Short matchSrcTcpUdpPort) { this.matchSrcTcpUdpPort = matchSrcTcpUdpPort; }
 		public void setMatchDstTcpUdpPortForTest(Short matchDstTcpUdpPort) { this.matchDstTcpUdpPort = matchDstTcpUdpPort; }
+		public void setActionsForTest(String actions) { this.actions = actions; }
 		public void addFlowEntryForTest(IFlowEntry entry) { entries.add(entry); }
 		public void addSwitchForTest(ISwitchObject sw) { switches.add(sw); }
 
@@ -693,6 +698,13 @@
 			matchDstTcpUdpPortToUpdate = matchDstTcpUdpPort; }
 
 		@Override
+		public String getActions() { return actions; }
+
+		@Override
+		public void setActions(String actions) {
+			actionsToUpdate = actions; }
+
+		@Override
 		public Iterable<ISwitchObject> getSwitches() { return switches; }
 
 		@Override
@@ -712,7 +724,8 @@
 		private String matchSrcIpaddr,matchDstIpaddr;
 		private Byte matchIpProto, matchIpToS;
 		private Short matchSrcTcpUdpPort, matchDstTcpUdpPort;
-		private Short actionOutput;
+		private Short actionOutputPort;
+		private String actions;
 		
 		private IFlowPath flowPath;
 		private ISwitchObject sw;
@@ -728,7 +741,8 @@
 		private String matchSrcIpaddrToUpdate,matchDstIpaddrToUpdate;
 		private Byte matchIpProtoToUpdate, matchIpToSToUpdate;
 		private Short matchSrcTcpUdpPortToUpdate, matchDstTcpUdpPortToUpdate;
-		private Short actionOutputToUpdate;
+		private Short actionOutputPortToUpdate;
+		private String actionsToUpdate;
 	
 		private IFlowPath flowPathToUpdate;
 		private ISwitchObject swToUpdate;
@@ -761,7 +775,8 @@
 			if(matchIpToSToUpdate != null) { matchIpToS = matchIpToSToUpdate; }
 			if(matchSrcTcpUdpPortToUpdate != null) { matchSrcTcpUdpPort = matchSrcTcpUdpPortToUpdate; }
 			if(matchDstTcpUdpPortToUpdate != null) { matchDstTcpUdpPort = matchDstTcpUdpPortToUpdate; }
-			if(actionOutputToUpdate != null) { actionOutput = actionOutputToUpdate; }
+			if(actionOutputPortToUpdate != null) { actionOutputPort = actionOutputPortToUpdate; }
+			if(actionsToUpdate != null) { actions = actionsToUpdate; }
 			
 			if(flowPathToUpdate != null) { flowPath = flowPathToUpdate; }
 			if(swToUpdate != null) { sw = swToUpdate; }
@@ -786,7 +801,8 @@
 			matchSrcIpaddrToUpdate = matchDstIpaddrToUpdate = null;
 			matchIpProtoToUpdate = matchIpToSToUpdate = null;
 			matchSrcTcpUdpPortToUpdate = matchDstTcpUdpPortToUpdate = null;
-			actionOutputToUpdate = null;
+			actionOutputPortToUpdate = null;
+			actionsToUpdate = null;
 			flowPathToUpdate = null;
 			swToUpdate = null;
 			inportToUpdate = outportToUpdate = null;
@@ -813,7 +829,8 @@
 		public void setMatchIpToSForTest(Byte matchIpToS) { this.matchIpToS = matchIpToS; }
 		public void setMatchSrcTcpUdpPortForTest(Short matchSrcTcpUdpPort) { this.matchSrcTcpUdpPort = matchSrcTcpUdpPort; }
 		public void setMatchDstTcpUdpPortForTest(Short matchDstTcpUdpPort) { this.matchDstTcpUdpPort = matchDstTcpUdpPort; }
-		public void setActionOutputForTest(Short actionOutput) { this.actionOutput = actionOutput; }
+		public void setActionOutputPortForTest(Short actionOutputPort) { this.actionOutputPort = actionOutputPort; }
+		public void setActionsForTest(String actions) { this.actions = actions; }
 		public void setFlowPathForTest(IFlowPath flowPath) { this.flowPath = flowPath; }
 		public void setSwitchForTest(ISwitchObject sw) { this.sw = sw; }
 		public void setInportForTest(IPortObject inport) { this.inport = inport; }
@@ -946,10 +963,16 @@
 		public void setMatchDstTcpUdpPort(Short matchDstTcpUdpPort) { matchDstTcpUdpPortToUpdate = matchDstTcpUdpPort; }
 	
 		@Override
-		public Short getActionOutput() { return actionOutput; }
+		public Short getActionOutputPort() { return actionOutputPort; }
 	
 		@Override
-		public void setActionOutput(Short actionOutput) { actionOutputToUpdate = actionOutput; }
+		public void setActionOutputPort(Short actionOutputPort) { actionOutputPortToUpdate = actionOutputPort; }
+
+		@Override
+		public String getActions() { return actions; }
+	
+		@Override
+		public void setActions(String actions) { actionsToUpdate = actions; }
 	
 		@Override
 		public IFlowPath getFlow() { return flowPath; }
diff --git a/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java b/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
index cacedd2..7bc0aac 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/flowmanager/FlowManagerTest.java
@@ -1086,7 +1086,7 @@
 		flowEntry1.setOutPort(new Port((short) 11));
 		flowEntry1.setFlowEntryId(new FlowEntryId(1));
 		flowEntry1.setFlowEntryMatch(new FlowEntryMatch());
-		flowEntry1.setFlowEntryActions(new ArrayList<FlowEntryAction>());
+		flowEntry1.setFlowEntryActions(new FlowEntryActions());
 		flowEntry1.setFlowEntryErrorState(new FlowEntryErrorState());
 		
 		FlowEntry flowEntry2 = new FlowEntry();
@@ -1096,7 +1096,7 @@
 		flowEntry2.setOutPort(new Port((short) 2));
 		flowEntry2.setFlowEntryId(new FlowEntryId(2));
 		flowEntry2.setFlowEntryMatch(new FlowEntryMatch());
-		flowEntry2.setFlowEntryActions(new ArrayList<FlowEntryAction>());
+		flowEntry2.setFlowEntryActions(new FlowEntryActions());
 		flowEntry2.setFlowEntryErrorState(new FlowEntryErrorState());
 		
 		DataPath dataPath = new DataPath();
@@ -1147,6 +1147,11 @@
 		// instantiate required objects
 		FlowManager fm = new FlowManager();
 		
+		FlowEntryAction action = new FlowEntryAction();
+		action.setActionOutput(new Port((short)2));
+		FlowEntryActions actions = new FlowEntryActions();
+		actions.addAction(action);
+
 		// setup expectations
 		expectInitWithContext();
 		expect(iFlowEntry.getFlowEntryId()).andReturn(new FlowEntryId(123).toString());
@@ -1164,7 +1169,7 @@
 		expect(iFlowEntry.getMatchIpToS()).andReturn(new Byte((byte)0x3));
 		expect(iFlowEntry.getMatchSrcTcpUdpPort()).andReturn(new Short((short)40000));
 		expect(iFlowEntry.getMatchDstTcpUdpPort()).andReturn(new Short((short)80));
-		expect(iFlowEntry.getActionOutput()).andReturn(new Short((short) 2));
+		expect(iFlowEntry.getActions()).andReturn(actions.toString());
 		expect(floodlightProvider.getOFMessageFactory()).andReturn(basicFactory);
 		expect(basicFactory.getMessage(OFType.FLOW_MOD)).andReturn(new OFFlowMod());
 		expect(iofSwitch.getStringId()).andReturn(new Dpid(100).toString());
diff --git a/src/test/java/net/onrc/onos/ofcontroller/routing/TopoRouteServiceTest.java b/src/test/java/net/onrc/onos/ofcontroller/routing/TopoRouteServiceTest.java
index ba23a01..a33df98 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/routing/TopoRouteServiceTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/routing/TopoRouteServiceTest.java
@@ -48,7 +48,6 @@
 /**
  * A class for testing the TopoRouteService class.
  * @see net.onrc.onos.ofcontroller.routing.TopoRouteService
- * @author Pavlin Radoslavov (pavlin@onlab.us)
  */
 @RunWith(PowerMockRunner.class)
 @PrepareForTest({TitanFactory.class, GraphDBConnection.class, GraphDBOperation.class, TopoRouteService.class})
diff --git a/start-onos.sh b/start-onos.sh
index 13ea0ac..a35d181 100755
--- a/start-onos.sh
+++ b/start-onos.sh
@@ -16,6 +16,8 @@
 
 # Set JVM options
 JVM_OPTS=""
+## If you want JaCoCo Code Coverage reports... uncomment line below
+JVM_OPTS="$JVM_OPTS -javaagent:${ONOS_HOME}/lib/jacocoagent.jar=dumponexit=true,output=file,destfile=${LOGDIR}/jacoco.exec"
 JVM_OPTS="$JVM_OPTS -server -d64"
 #JVM_OPTS="$JVM_OPTS -Xmx2g -Xms2g -Xmn800m"
 JVM_OPTS="$JVM_OPTS -Xmx1g -Xms1g -Xmn800m"
@@ -134,7 +136,7 @@
   pids="$flpid $tdpid"
   for p in ${pids}; do
     if [ x$p != "x" ]; then
-      kill -KILL $p
+      kill -TERM $p
       echo "Killed existing process (pid: $p)"
     fi
   done
diff --git a/web/add_flow.py b/web/add_flow.py
index f61e926..8100f22 100755
--- a/web/add_flow.py
+++ b/web/add_flow.py
@@ -204,18 +204,19 @@
       match['dstTcpUdpPort'] = int(arg2, 0)
       # match['matchDstTcpUdpPort'] = True
     elif arg1 == "actionOutput":
-      # Just mark whether ACTION_OUTPUT action is enabled
+      # Mark whether ACTION_OUTPUT action is enabled
       actionOutputEnabled = arg2 in ['True', 'true']
-      #
-      # TODO: Complete the implementation for ACTION_OUTPUT
-      #   actionOutput = {}
-      #   outPort = {}
-      #   outPort['value'] = int(arg2, 0)
-      #   actionOutput['port'] = outPort
-      #   actionOutput['maxLen'] = int(arg3, 0)
-      #   action['actionOutput'] = actionOutput
-      #   # action['actionType'] = 'ACTION_OUTPUT'
-      #   actions.append(action)
+      # If ACTION_OUTPUT is explicitly enabled, add an entry with a fake
+      # port number. We need this entry to preserve the action ordering.
+      if actionOutputEnabled == True:
+        actionOutput = {}
+        outPort = {}
+        outPort['value'] = 0xffff
+        actionOutput['port'] = outPort
+        actionOutput['maxLen'] = 0
+        action['actionOutput'] = actionOutput
+        # action['actionType'] = 'ACTION_OUTPUT'
+        actions.append(action)
       #
     elif arg1 == "actionSetVlanId":
       vlanId = {}
@@ -346,6 +347,8 @@
   flowPathFlags = {}
   flowPathFlags['flags'] = myFlowPathFlags
 
+  flowEntryActions = {}
+
   flow_path = {}
   flow_path['flowId'] = flow_id
   flow_path['installerId'] = installer_id
@@ -367,6 +370,11 @@
       my_data_path['flowEntries'][idx]['flowEntryMatch'] = copy.deepcopy(match)
       idx = idx + 1
 
+
+  if (len(actions) > 0):
+    flowEntryActions['actions'] = copy.deepcopy(actions)
+    flow_path['flowEntryActions'] = flowEntryActions
+
   #
   # Set the actions for each flow entry
   # NOTE: The actions from the command line are aplied
@@ -388,11 +396,12 @@
       action['actionOutput'] = copy.deepcopy(actionOutput)
       # action['actionType'] = 'ACTION_OUTPUT'
       actions.append(copy.deepcopy(action))
+      flowEntryActions = {}
+      flowEntryActions['actions'] = copy.deepcopy(actions)
 
-      my_data_path['flowEntries'][idx]['flowEntryActions'] = copy.deepcopy(actions)
+      my_data_path['flowEntries'][idx]['flowEntryActions'] = flowEntryActions
       idx = idx + 1
 
-
   flow_path['dataPath'] = my_data_path
   debug("Flow Path: %s" % flow_path)
   return flow_path
@@ -506,18 +515,17 @@
   usage_msg = usage_msg + "\n"
   usage_msg = usage_msg + "    Actions:\n"
   usage_msg = usage_msg + "        actionOutput <True|False> (default to True)\n"
+  usage_msg = usage_msg + "        actionSetVlanId <VLAN ID>\n"
+  usage_msg = usage_msg + "        actionSetVlanPriority <VLAN priority>\n"
+  usage_msg = usage_msg + "        actionStripVlan <True|False>\n"
   usage_msg = usage_msg + "        actionSetEthernetSrcAddr <source MAC address>\n"
   usage_msg = usage_msg + "        actionSetEthernetDstAddr <destination MAC address>\n"
   usage_msg = usage_msg + "        actionSetIPv4SrcAddr <source IPv4 address>\n"
   usage_msg = usage_msg + "        actionSetIPv4DstAddr <destination IPv4 address>\n"
-  usage_msg = usage_msg + "\n"
-  usage_msg = usage_msg + "    Actions (not implemented yet):\n"
-  usage_msg = usage_msg + "        actionSetVlanId <VLAN ID>\n"
-  usage_msg = usage_msg + "        actionSetVlanPriority <VLAN priority>\n"
   usage_msg = usage_msg + "        actionSetIpToS <IP ToS (DSCP field, 6 bits)>\n"
   usage_msg = usage_msg + "        actionSetTcpUdpSrcPort <source TCP/UDP port>\n"
   usage_msg = usage_msg + "        actionSetTcpUdpDstPort <destination TCP/UDP port>\n"
-  usage_msg = usage_msg + "        actionStripVlan <True|False>\n"
+  usage_msg = usage_msg + "    Actions (not implemented yet):\n"
   usage_msg = usage_msg + "        actionEnqueue <dummy argument>\n"
 
   # app.debug = False;
diff --git a/web/get_flow.py b/web/get_flow.py
index 6599360..9ab55da 100755
--- a/web/get_flow.py
+++ b/web/get_flow.py
@@ -33,6 +33,131 @@
 # Sample output:
 # {"flowId":{"value":"0x5"},"installerId":{"value":"FOOBAR"},"dataPath":{"srcPort":{"dpid":{"value":"00:00:00:00:00:00:00:01"},"port":{"value":0}},"dstPort":{"dpid":{"value":"00:00:00:00:00:00:00:02"},"port":{"value":0}},"flowEntries":[{"flowEntryId":"0x1389","flowEntryMatch":null,"flowEntryActions":null,"dpid":{"value":"00:00:00:00:00:00:00:01"},"inPort":{"value":0},"outPort":{"value":1},"flowEntryUserState":"FE_USER_DELETE","flowEntrySwitchState":"FE_SWITCH_NOT_UPDATED","flowEntryErrorState":null},{"flowEntryId":"0x138a","flowEntryMatch":null,"flowEntryActions":null,"dpid":{"value":"00:00:00:00:00:00:00:02"},"inPort":{"value":9},"outPort":{"value":0},"flowEntryUserState":"FE_USER_DELETE","flowEntrySwitchState":"FE_SWITCH_NOT_UPDATED","flowEntryErrorState":null}]}}
 
+def parse_match(match):
+  result = []
+
+  inPort = match['inPort']
+  matchInPort = match['matchInPort']
+  srcMac = match['srcMac']
+  matchSrcMac = match['matchSrcMac']
+  dstMac = match['dstMac']
+  matchDstMac = match['matchDstMac']
+  ethernetFrameType = match['ethernetFrameType']
+  matchEthernetFrameType = match['matchEthernetFrameType']
+  vlanId = match['vlanId']
+  matchVlanId = match['matchVlanId']
+  vlanPriority = match['vlanPriority']
+  matchVlanPriority = match['matchVlanPriority']
+  srcIPv4Net = match['srcIPv4Net']
+  matchSrcIPv4Net = match['matchSrcIPv4Net']
+  dstIPv4Net = match['dstIPv4Net']
+  matchDstIPv4Net = match['matchDstIPv4Net']
+  ipProto = match['ipProto']
+  matchIpProto = match['matchIpProto']
+  ipToS = match['ipToS']
+  matchIpToS = match['matchIpToS']
+  srcTcpUdpPort = match['srcTcpUdpPort']
+  matchSrcTcpUdpPort = match['matchSrcTcpUdpPort']
+  dstTcpUdpPort = match['dstTcpUdpPort']
+  matchDstTcpUdpPort = match['matchDstTcpUdpPort']
+  if matchInPort == True:
+    r = "inPort: %s" % inPort['value']
+    result.append(r)
+  if matchSrcMac == True:
+    r = "srcMac: %s" % srcMac['value']
+    result.append(r)
+  if matchDstMac == True:
+    r = "dstMac: %s" % dstMac['value']
+    result.append(r)
+  if matchEthernetFrameType == True:
+    r = "ethernetFrameType: %s" % hex(ethernetFrameType)
+    result.append(r)
+  if matchVlanId == True:
+    r = "vlanId: %s" % vlanId
+    result.append(r)
+  if matchVlanPriority == True:
+    r = "vlanPriority: %s" % vlanPriority
+    result.append(r)
+  if matchSrcIPv4Net == True:
+    r = "srcIPv4Net: %s" % srcIPv4Net['value']
+    result.append(r)
+  if matchDstIPv4Net == True:
+    r = "dstIPv4Net: %s" % dstIPv4Net['value']
+    result.append(r)
+  if matchIpProto == True:
+    r = "ipProto: %s" % ipProto
+    result.append(r)
+  if matchIpToS == True:
+    r = "ipToS: %s" % ipToS
+    result.append(r)
+  if matchSrcTcpUdpPort == True:
+    r = "srcTcpUdpPort: %s" % srcTcpUdpPort
+    result.append(r)
+  if matchDstTcpUdpPort == True:
+    r = "dstTcpUdpPort: %s" % dstTcpUdpPort
+    result.append(r)
+
+  return result
+
+
+def parse_actions(actions):
+  result = []
+  for a in actions:
+    actionType = a['actionType']
+    if actionType == "ACTION_OUTPUT":
+      port = a['actionOutput']['port']['value']
+      maxLen = a['actionOutput']['maxLen']
+      r = "actionType: %s port: %s maxLen: %s" % (actionType, port, maxLen)
+      result.append(r)
+    if actionType == "ACTION_SET_VLAN_VID":
+      vlanId = a['actionSetVlanId']['vlanId']
+      r = "actionType: %s vlanId: %s" % (actionType, vlanId)
+      result.append(r)
+    if actionType == "ACTION_SET_VLAN_PCP":
+      vlanPriority = a['actionSetVlanPriority']['vlanPriority']
+      r = "actionType: %s vlanPriority: %s" % (actionType, vlanPriority)
+      result.append(r)
+    if actionType == "ACTION_STRIP_VLAN":
+      stripVlan = a['actionStripVlan']['stripVlan']
+      r = "actionType: %s stripVlan: %s" % (actionType, stripVlan)
+      result.append(r)
+    if actionType == "ACTION_SET_DL_SRC":
+      setEthernetSrcAddr = a['actionSetEthernetSrcAddr']['addr']['value']
+      r = "actionType: %s setEthernetSrcAddr: %s" % (actionType, setEthernetSrcAddr)
+      result.append(r)
+    if actionType == "ACTION_SET_DL_DST":
+      setEthernetDstAddr = a['actionSetEthernetDstAddr']['addr']['value']
+      r = "actionType: %s setEthernetDstAddr: %s" % (actionType, setEthernetDstAddr)
+      result.append(r)
+    if actionType == "ACTION_SET_NW_SRC":
+      setIPv4SrcAddr = a['actionSetIPv4SrcAddr']['addr']['value']
+      r = "actionType: %s setIPv4SrcAddr: %s" % (actionType, setIPv4SrcAddr)
+      result.append(r)
+    if actionType == "ACTION_SET_NW_DST":
+      setIPv4DstAddr = a['actionSetIPv4DstAddr']['addr']['value']
+      r = "actionType: %s setIPv4DstAddr: %s" % (actionType, setIPv4DstAddr)
+      result.append(r)
+    if actionType == "ACTION_SET_NW_TOS":
+      setIpToS = a['actionSetIpToS']['ipToS']
+      r = "actionType: %s setIpToS: %s" % (actionType, setIpToS)
+      result.append(r)
+    if actionType == "ACTION_SET_TP_SRC":
+      setTcpUdpSrcPort = a['actionSetTcpUdpSrcPort']['port']
+      r = "actionType: %s setTcpUdpSrcPort: %s" % (actionType, setTcpUdpSrcPort)
+      result.append(r)
+    if actionType == "ACTION_SET_TP_DST":
+      setTcpUdpDstPort = a['actionSetTcpUdpDstPort']['port']
+      r = "actionType: %s setTcpUdpDstPort: %s" % (actionType, setTcpUdpDstPort)
+      result.append(r)
+    if actionType == "ACTION_ENQUEUE":
+      port = a['actionEnqueue']['port']['value']
+      queueId = a['actionEnqueue']['queueId']
+      r = "actionType: %s port: %s queueId: %s" % (actionType, port, queueId)
+      result.append(r)
+
+  return result
+
+
 def print_flow_path(parsedResult):
   flowId = parsedResult['flowId']['value']
   installerId = parsedResult['installerId']['value']
@@ -41,6 +166,8 @@
   srcPort = parsedResult['dataPath']['srcPort']['port']['value']
   dstSwitch = parsedResult['dataPath']['dstPort']['dpid']['value']
   dstPort = parsedResult['dataPath']['dstPort']['port']['value']
+  match = parsedResult['flowEntryMatch'];
+  actions = parsedResult['flowEntryActions']['actions']
 
   flowPathFlagsStr = ""
   if (flowPathFlags & 0x1):
@@ -53,69 +180,35 @@
     flowPathFlagsStr += "KEEP_ONLY_FIRST_HOP_ENTRY"
 
   print "FlowPath: (flowId = %s installerId = %s flowPathFlags = 0x%x(%s) src = %s/%s dst = %s/%s)" % (flowId, installerId, flowPathFlags, flowPathFlagsStr, srcSwitch, srcPort, dstSwitch, dstPort)
-  match = parsedResult['flowEntryMatch'];
+
   #
-  # Print the common conditions
+  # Print the common match conditions
   #
   if match == None:
     print "   Match: %s" % (match)
   else:
-    # inPort = match['inPort']
-    # matchInPort = match['matchInPort']
-    srcMac = match['srcMac']
-    matchSrcMac = match['matchSrcMac']
-    dstMac = match['dstMac']
-    matchDstMac = match['matchDstMac']
-    ethernetFrameType = match['ethernetFrameType']
-    matchEthernetFrameType = match['matchEthernetFrameType']
-    vlanId = match['vlanId']
-    matchVlanId = match['matchVlanId']
-    vlanPriority = match['vlanPriority']
-    matchVlanPriority = match['matchVlanPriority']
-    srcIPv4Net = match['srcIPv4Net']
-    matchSrcIPv4Net = match['matchSrcIPv4Net']
-    dstIPv4Net = match['dstIPv4Net']
-    matchDstIPv4Net = match['matchDstIPv4Net']
-    ipProto = match['ipProto']
-    matchIpProto = match['matchIpProto']
-    ipToS = match['ipToS']
-    matchIpToS = match['matchIpToS']
-    srcTcpUdpPort = match['srcTcpUdpPort']
-    matchSrcTcpUdpPort = match['matchSrcTcpUdpPort']
-    dstTcpUdpPort = match['dstTcpUdpPort']
-    matchDstTcpUdpPort = match['matchDstTcpUdpPort']
-    # if matchInPort == True:
-    #  print "    inPort: %s" % inPort['value']
-    if matchSrcMac == True:
-      print "    srcMac: %s" % srcMac['value']
-    if matchDstMac == True:
-      print "    dstMac: %s" % dstMac['value']
-    if matchEthernetFrameType == True:
-      print "    ethernetFrameType: %s" % hex(ethernetFrameType)
-    if matchVlanId == True:
-      print "    vlanId: %s" % vlanId
-    if matchVlanPriority == True:
-      print "    vlanPriority: %s" % vlanPriority
-    if matchSrcIPv4Net == True:
-      print "    srcIPv4Net: %s" % srcIPv4Net['value']
-    if matchDstIPv4Net == True:
-      print "    dstIPv4Net: %s" % dstIPv4Net['value']
-    if matchIpProto == True:
-      print "    ipProto: %s" % ipProto
-    if matchIpToS == True:
-      print "    ipToS: %s" % ipToS
-    if matchSrcTcpUdpPort == True:
-      print "    srcTcpUdpPort: %s" % srcTcpUdpPort
-    if matchDstTcpUdpPort == True:
-      print "    dstTcpUdpPort: %s" % dstTcpUdpPort
+    parsedMatch = parse_match(match)
+    for l in parsedMatch:
+      print "    %s" % l
 
+  #
+  # Print the actions
+  #
+  parsedActions = parse_actions(actions)
+  for l in parsedActions:
+    print "    %s" % l
+
+  #
+  # Print each Flow Entry
+  #
   for f in parsedResult['dataPath']['flowEntries']:
     flowEntryId = f['flowEntryId']
     dpid = f['dpid']['value']
     userState = f['flowEntryUserState']
     switchState = f['flowEntrySwitchState']
     match = f['flowEntryMatch'];
-    actions = f['flowEntryActions']
+    actions = f['flowEntryActions']['actions']
+
     print "  FlowEntry: (%s, %s, %s, %s)" % (flowEntryId, dpid, userState, switchState)
 
     #
@@ -124,101 +217,16 @@
     if match == None:
       print "   Match: %s" % (match)
     else:
-      inPort = match['inPort']
-      matchInPort = match['matchInPort']
-      srcMac = match['srcMac']
-      matchSrcMac = match['matchSrcMac']
-      dstMac = match['dstMac']
-      matchDstMac = match['matchDstMac']
-      ethernetFrameType = match['ethernetFrameType']
-      matchEthernetFrameType = match['matchEthernetFrameType']
-      vlanId = match['vlanId']
-      matchVlanId = match['matchVlanId']
-      vlanPriority = match['vlanPriority']
-      matchVlanPriority = match['matchVlanPriority']
-      srcIPv4Net = match['srcIPv4Net']
-      matchSrcIPv4Net = match['matchSrcIPv4Net']
-      dstIPv4Net = match['dstIPv4Net']
-      matchDstIPv4Net = match['matchDstIPv4Net']
-      ipProto = match['ipProto']
-      matchIpProto = match['matchIpProto']
-      ipToS = match['ipToS']
-      matchIpToS = match['matchIpToS']
-      srcTcpUdpPort = match['srcTcpUdpPort']
-      matchSrcTcpUdpPort = match['matchSrcTcpUdpPort']
-      dstTcpUdpPort = match['dstTcpUdpPort']
-      matchDstTcpUdpPort = match['matchDstTcpUdpPort']
-      if matchInPort == True:
-	print "    inPort: %s" % inPort['value']
-      if matchSrcMac == True:
-	print "    srcMac: %s" % srcMac['value']
-      if matchDstMac == True:
-	print "    dstMac: %s" % dstMac['value']
-      if matchEthernetFrameType == True:
-	print "    ethernetFrameType: %s" % hex(ethernetFrameType)
-      if matchVlanId == True:
-	print "    vlanId: %s" % vlanId
-      if matchVlanPriority == True:
-	print "    vlanPriority: %s" % vlanPriority
-      if matchSrcIPv4Net == True:
-	print "    srcIPv4Net: %s" % srcIPv4Net['value']
-      if matchDstIPv4Net == True:
-	print "    dstIPv4Net: %s" % dstIPv4Net['value']
-      if matchIpProto == True:
-	print "    ipProto: %s" % ipProto
-      if matchIpToS == True:
-	print "    ipToS: %s" % ipToS
-      if matchSrcTcpUdpPort == True:
-	print "    srcTcpUdpPort: %s" % srcTcpUdpPort
-      if matchDstTcpUdpPort == True:
-	print "    dstTcpUdpPort: %s" % dstTcpUdpPort
-
+      parsedMatch = parse_match(match)
+      for l in parsedMatch:
+	print "    %s" % l
     #
     # Print the actions
     #
-    if actions == None:
-      print "   Actions: %s" % (actions)
-    else:
-      for a in actions:
-	actionType = a['actionType']
-	if actionType == "ACTION_OUTPUT":
-	  port = a['actionOutput']['port']['value']
-	  maxLen = a['actionOutput']['maxLen']
-	  print "    actionType: %s port: %s maxLen: %s" % (actionType, port, maxLen)
-	if actionType == "ACTION_SET_VLAN_VID":
-	  vlanId = a['actionSetVlanId']['vlanId']
-	  print "    actionType: %s vlanId: %s" % (actionType, vlanId)
-	if actionType == "ACTION_SET_VLAN_PCP":
-	  vlanPriority = a['actionSetVlanPriority']['vlanPriority']
-	  print "    actionType: %s vlanPriority: %s" % (actionType, vlanPriority)
-	if actionType == "ACTION_STRIP_VLAN":
-	  stripVlan = a['actionStripVlan']['stripVlan']
-	  print "    actionType: %s stripVlan: %s" % (actionType, stripVlan)
-	if actionType == "ACTION_SET_DL_SRC":
-	  setEthernetSrcAddr = a['actionSetEthernetSrcAddr']['addr']['value']
-	  print "    actionType: %s setEthernetSrcAddr: %s" % (actionType, setEthernetSrcAddr)
-	if actionType == "ACTION_SET_DL_DST":
-	  setEthernetDstAddr = a['actionSetEthernetDstAddr']['addr']['value']
-	  print "    actionType: %s setEthernetDstAddr: %s" % (actionType, setEthernetDstAddr)
-	if actionType == "ACTION_SET_NW_SRC":
-	  setIPv4SrcAddr = a['actionSetIPv4SrcAddr']['addr']['value']
-	  print "    actionType: %s setIPv4SrcAddr: %s" % (actionType, setIPv4SrcAddr)
-	if actionType == "ACTION_SET_NW_DST":
-	  setIPv4DstAddr = a['actionSetIPv4DstAddr']['addr']['value']
-	  print "    actionType: %s setIPv4DstAddr: %s" % (actionType, setIPv4DstAddr)
-	if actionType == "ACTION_SET_NW_TOS":
-	  setIpToS = a['actionSetIpToS']['ipToS']
-	  print "    actionType: %s setIpToS: %s" % (actionType, setIpToS)
-	if actionType == "ACTION_SET_TP_SRC":
-	  setTcpUdpSrcPort = a['actionSetTcpUdpSrcPort']['port']
-	  print "    actionType: %s setTcpUdpSrcPort: %s" % (actionType, setTcpUdpSrcPort)
-	if actionType == "ACTION_SET_TP_DST":
-	  setTcpUdpDstPort = a['actionSetTcpUdpDstPort']['port']
-	  print "    actionType: %s setTcpUdpDstPort: %s" % (actionType, setTcpUdpDstPort)
-	if actionType == "ACTION_ENQUEUE":
-	  port = a['actionEnqueue']['port']['value']
-	  queueId = a['actionEnqueue']['queueId']
-	  print "    actionType: %s port: %s queueId: %s" % (actionType, port, queueId)
+    parsedActions = parse_actions(actions)
+    for l in parsedActions:
+      print "    %s" % l
+
 
 def get_flow_path(flow_id):
   try:
diff --git a/web/measurement_store_flow.py b/web/measurement_store_flow.py
index 8f6bd73..0e39465 100755
--- a/web/measurement_store_flow.py
+++ b/web/measurement_store_flow.py
@@ -87,7 +87,7 @@
       if "KEEP_ONLY_FIRST_HOP_ENTRY" in arg2:
 	flowPathFlags = flowPathFlags + 0x2
     elif arg1 == "matchInPort":
-      # Just mark whether inPort matching is enabled
+      # Mark whether ACTION_OUTPUT action is enabled
       matchInPortEnabled = arg2 in ['True', 'true']
       # inPort = {}
       # inPort['value'] = int(arg2, 0)
@@ -135,18 +135,19 @@
       match['dstTcpUdpPort'] = int(arg2, 0)
       # match['matchDstTcpUdpPort'] = True
     elif arg1 == "actionOutput":
-      # Just mark whether ACTION_OUTPUT action is enabled
+      # Mark whether ACTION_OUTPUT action is enabled
       actionOutputEnabled = arg2 in ['True', 'true']
-      #
-      # TODO: Complete the implementation for ACTION_OUTPUT
-      #   actionOutput = {}
-      #   outPort = {}
-      #   outPort['value'] = int(arg2, 0)
-      #   actionOutput['port'] = outPort
-      #   actionOutput['maxLen'] = int(arg3, 0)
-      #   action['actionOutput'] = actionOutput
-      #   # action['actionType'] = 'ACTION_OUTPUT'
-      #   actions.append(action)
+      # If ACTION_OUTPUT is explicitly enabled, add an entry with a fake
+      # port number. We need this entry to preserve the action ordering.
+      if actionOutputEnabled == True:
+        actionOutput = {}
+        outPort = {}
+        outPort['value'] = 0xffff
+        actionOutput['port'] = outPort
+        actionOutput['maxLen'] = 0
+        action['actionOutput'] = actionOutput
+        # action['actionType'] = 'ACTION_OUTPUT'
+        actions.append(action)
       #
     elif arg1 == "actionSetVlanId":
       vlanId = {}
@@ -264,6 +265,8 @@
   flowPathFlags = {}
   flowPathFlags['flags'] = myFlowPathFlags
 
+  flowEntryActions = {}
+
   flow_path = {}
   flow_path['flowId'] = flow_id
   flow_path['installerId'] = installer_id
@@ -285,6 +288,11 @@
       my_data_path['flowEntries'][idx]['flowEntryMatch'] = copy.deepcopy(match)
       idx = idx + 1
 
+
+  if (len(actions) > 0):
+    flowEntryActions['actions'] = copy.deepcopy(actions)
+    flow_path['flowEntryActions'] = flowEntryActions
+
   #
   # Set the actions for each flow entry
   # NOTE: The actions from the command line are aplied
@@ -306,11 +314,12 @@
       action['actionOutput'] = copy.deepcopy(actionOutput)
       # action['actionType'] = 'ACTION_OUTPUT'
       actions.append(copy.deepcopy(action))
+      flowEntryActions = {}
+      flowEntryActions['actions'] = copy.deepcopy(actions)
 
-      my_data_path['flowEntries'][idx]['flowEntryActions'] = copy.deepcopy(actions)
+      my_data_path['flowEntries'][idx]['flowEntryActions'] = flowEntryActions
       idx = idx + 1
 
-
   flow_path['dataPath'] = my_data_path
   debug("Flow Path: %s" % flow_path)
   return flow_path
@@ -386,18 +395,17 @@
   usage_msg = usage_msg + "\n"
   usage_msg = usage_msg + "    Actions:\n"
   usage_msg = usage_msg + "        actionOutput <True|False> (default to True)\n"
+  usage_msg = usage_msg + "        actionSetVlanId <VLAN ID>\n"
+  usage_msg = usage_msg + "        actionSetVlanPriority <VLAN priority>\n"
+  usage_msg = usage_msg + "        actionStripVlan <True|False>\n"
   usage_msg = usage_msg + "        actionSetEthernetSrcAddr <source MAC address>\n"
   usage_msg = usage_msg + "        actionSetEthernetDstAddr <destination MAC address>\n"
   usage_msg = usage_msg + "        actionSetIPv4SrcAddr <source IPv4 address>\n"
   usage_msg = usage_msg + "        actionSetIPv4DstAddr <destination IPv4 address>\n"
-  usage_msg = usage_msg + "\n"
-  usage_msg = usage_msg + "    Actions (not implemented yet):\n"
-  usage_msg = usage_msg + "        actionSetVlanId <VLAN ID>\n"
-  usage_msg = usage_msg + "        actionSetVlanPriority <VLAN priority>\n"
   usage_msg = usage_msg + "        actionSetIpToS <IP ToS (DSCP field, 6 bits)>\n"
   usage_msg = usage_msg + "        actionSetTcpUdpSrcPort <source TCP/UDP port>\n"
   usage_msg = usage_msg + "        actionSetTcpUdpDstPort <destination TCP/UDP port>\n"
-  usage_msg = usage_msg + "        actionStripVlan <True|False>\n"
+  usage_msg = usage_msg + "    Actions (not implemented yet):\n"
   usage_msg = usage_msg + "        actionEnqueue <dummy argument>\n"
 
   # app.debug = False;
diff --git a/web/topology_rest.py b/web/topology_rest.py
index 53f46bc..ea33a00 100755
--- a/web/topology_rest.py
+++ b/web/topology_rest.py
@@ -216,19 +216,6 @@
   resp = Response(result, status=200, mimetype='application/json')
   return resp
 
-@app.route("/proxy/gui/switchctrl/<cmd>")
-def proxy_switch_controller_setting(cmd):
-  try:
-    command = "curl -s %s/gui/switchctrl/%s" % (ONOS_GUI3_CONTROL_HOST, cmd)
-    print command
-    result = os.popen(command).read()
-  except:
-    print "REST IF has issue"
-    exit
-
-  resp = Response(result, status=200, mimetype='application/json')
-  return resp
-
 @app.route("/proxy/gui/reset")
 def proxy_gui_reset():
   result = ""