diff --git a/build.xml b/build.xml
index 003ec3a939dfc1d9912ba7bae0f571ce62fa8d0e..eebe6287105140721e956fc638266266e20ffe2b 100644
--- a/build.xml
+++ b/build.xml
@@ -78,8 +78,9 @@
         <include name="findbugs-annotations-2.0.1.jar" />
         <include name="findbugs-jsr305-2.0.1.jar" />
         <include name="derby-10.9.1.0.jar"/>
-    	<include name="openflowj-2.0.0-SNAPSHOT.jar"/>
-    	<include name="openflowj-2.0.0-SNAPSHOT-javadoc.jar"/>
+    	<include name="openflowj-3.0.0-SNAPSHOT.jar"/>
+    	<include name="openflowj-3.0.0-SNAPSHOT-javadoc.jar"/>
+	<include name="openflowj-3.0.0-SNAPSHOT-sources.jar"/>
     	<include name="hamcrest-core-1.3.jar"/>
     	<include name="hamcrest-integration-1.3.jar"/>
     	<include name="hamcrest-library-1.3.jar"/>
diff --git a/lib/asm-3.0.jar b/lib/asm-3.0.jar
deleted file mode 100644
index 112f5bd4aecd5f9b7b3540f2c1a2a67b552e1d8b..0000000000000000000000000000000000000000
Binary files a/lib/asm-3.0.jar and /dev/null differ
diff --git a/lib/asm-tree-3.0.jar b/lib/asm-tree-3.0.jar
deleted file mode 100644
index 2a4b20856c0cff5a70575665d4583ef5ace0eed6..0000000000000000000000000000000000000000
Binary files a/lib/asm-tree-3.0.jar and /dev/null differ
diff --git a/lib/cobertura-1.9.4.1.jar b/lib/cobertura-1.9.4.1.jar
deleted file mode 100644
index b7332491cdac15e27f1dab3fe22bc6b811f089f1..0000000000000000000000000000000000000000
Binary files a/lib/cobertura-1.9.4.1.jar and /dev/null differ
diff --git a/lib/jdeb-1.0.1.jar b/lib/jdeb-1.0.1.jar
deleted file mode 100644
index edef380c742c99178d03cd4ebd0cddc6aea5ab60..0000000000000000000000000000000000000000
Binary files a/lib/jdeb-1.0.1.jar and /dev/null differ
diff --git a/lib/log4j-1.2.9.jar b/lib/log4j-1.2.9.jar
deleted file mode 100644
index a6568b01a2179f5b20473a1dcee4c67da062b327..0000000000000000000000000000000000000000
Binary files a/lib/log4j-1.2.9.jar and /dev/null differ
diff --git a/lib/openflowj-2.0.0-SNAPSHOT-javadoc.jar b/lib/openflowj-2.0.0-SNAPSHOT-javadoc.jar
deleted file mode 100644
index 25f13543563de36b77d132b3c5434728fc7395bd..0000000000000000000000000000000000000000
Binary files a/lib/openflowj-2.0.0-SNAPSHOT-javadoc.jar and /dev/null differ
diff --git a/lib/openflowj-2.0.0-SNAPSHOT-sources.jar b/lib/openflowj-2.0.0-SNAPSHOT-sources.jar
deleted file mode 100644
index f2403b8ef0d5ab3f73ba02ffa09690909da8d786..0000000000000000000000000000000000000000
Binary files a/lib/openflowj-2.0.0-SNAPSHOT-sources.jar and /dev/null differ
diff --git a/lib/openflowj-3.0.0-SNAPSHOT-javadoc.jar b/lib/openflowj-3.0.0-SNAPSHOT-javadoc.jar
new file mode 100644
index 0000000000000000000000000000000000000000..8b34bf9bca5d12c9a9a612efed973797133c758b
Binary files /dev/null and b/lib/openflowj-3.0.0-SNAPSHOT-javadoc.jar differ
diff --git a/lib/openflowj-3.0.0-SNAPSHOT-sources.jar b/lib/openflowj-3.0.0-SNAPSHOT-sources.jar
new file mode 100644
index 0000000000000000000000000000000000000000..929e5b0807c1021880a1452f77059ed7dc700ee2
Binary files /dev/null and b/lib/openflowj-3.0.0-SNAPSHOT-sources.jar differ
diff --git a/lib/openflowj-2.0.0-SNAPSHOT.jar b/lib/openflowj-3.0.0-SNAPSHOT.jar
similarity index 54%
rename from lib/openflowj-2.0.0-SNAPSHOT.jar
rename to lib/openflowj-3.0.0-SNAPSHOT.jar
index 7f25d80d801bfca393c2ea807f9df8349c418fac..660a8d27df315ef74ca665d1722d48ef2f652f7e 100644
Binary files a/lib/openflowj-2.0.0-SNAPSHOT.jar and b/lib/openflowj-3.0.0-SNAPSHOT.jar differ
diff --git a/lib/oro/jakarta-oro-2.0.8.jar b/lib/oro/jakarta-oro-2.0.8.jar
deleted file mode 100644
index 23488d2600f5f4784c0ba2be5baa4c41f396f616..0000000000000000000000000000000000000000
Binary files a/lib/oro/jakarta-oro-2.0.8.jar and /dev/null differ
diff --git a/logback.xml b/logback.xml
index 0485717cddefe39bd795685712cce3e4c0af18ad..046352aebf359b9da910aac0983e8448ab404ed1 100644
--- a/logback.xml
+++ b/logback.xml
@@ -4,11 +4,11 @@
       <pattern>%date{yyyy-MM-dd HH:mm:ss.S} %-5level [%logger{15}] %msg%n</pattern>
     </encoder>
   </appender>
-  <root level="DEBUG">
+  <root level="INFO">
     <appender-ref ref="STDOUT" />
   </root>
   <logger name="io" level="INFO"></logger> <!-- Netty logging -->
-  <logger name="LogService" level="DEBUG"/> <!-- Restlet access logging -->
+  <logger name="LogService" level="INFO"/> <!-- Restlet access logging -->
   <logger name="net.floodlightcontroller" level="INFO"/>
   <logger name="org.sdnplatform" level="INFO"/>
 </configuration>
diff --git a/pom.xml b/pom.xml
index 778e60b7fd7cc91971570850abea580e6d128337..9c4867186ae42d6535a756777b54852a8932a0b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -212,7 +212,7 @@
 		<dependency>
 			<groupId>org.projectfloodlight</groupId>
 			<artifactId>openflowj</artifactId>
-			<version>2.0.0-SNAPSHOT</version>
+			<version>3.0.0-SNAPSHOT</version>
 		</dependency>
 		<dependency>
 			<groupId>io.netty</groupId>
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
index 5aa57b8c2c1d004f4e9cfbf5b1f53bb60e12642a..7b02d77ef035edbd5f464417091feef72b278eb9 100644
--- a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
@@ -44,9 +44,10 @@ import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.devicemanager.SwitchPort;
 import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.restserver.IRestApiService;
-import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
+import net.floodlightcontroller.staticentry.StaticEntryPusher;
 import net.floodlightcontroller.storage.IStorageSourceService;
 
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
 import org.projectfloodlight.openflow.types.IPv4Address;
 import org.projectfloodlight.openflow.util.HexString;
 import org.slf4j.Logger;
@@ -173,7 +174,7 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
 
 		for (Set<String> flowNameSet : ruleId2FlowName.values()) {
 			for (String flowName : flowNameSet) {
-				storageSource.deleteRowAsync("controller_staticflowtableentry",
+				storageSource.deleteRowAsync(StaticEntryPusher.TABLE_NAME,
 						flowName);
 				logger.debug("ACL flow(id:{}) is removed.", flowName);
 			}
@@ -212,7 +213,7 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
 	private void enforceRemovedRule(int ruleId) {
 		if (ruleId2FlowName.containsKey(ruleId)) {
 			for (String flowName : ruleId2FlowName.get(ruleId)) {
-				storageSource.deleteRowAsync("controller_staticflowtableentry",
+				storageSource.deleteRowAsync(StaticEntryPusher.TABLE_NAME,
 						flowName);
 				logger.debug("ACL flow(id:{}) is removed.", flowName);
 			}
@@ -236,66 +237,66 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
 
 			HashMap<String, Object> flow = new HashMap<String, Object>();
 
-			flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, dpid);
-			flow.put(StaticFlowEntryPusher.COLUMN_NAME, flowName);
-			flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE,
+			flow.put(StaticEntryPusher.Columns.COLUMN_SWITCH, dpid);
+			flow.put(StaticEntryPusher.Columns.COLUMN_NAME, flowName);
+			flow.put(StaticEntryPusher.Columns.COLUMN_ACTIVE,
 					Boolean.toString(true));
-			flow.put(StaticFlowEntryPusher.COLUMN_COOKIE, "0");
-			flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY,
+			flow.put(StaticEntryPusher.Columns.COLUMN_COOKIE, "0");
+			flow.put(StaticEntryPusher.Columns.COLUMN_PRIORITY,
 					Integer.toString(priority));
 
-			flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048");
-			flow.put(StaticFlowEntryPusher.COLUMN_NW_SRC, rule.getNw_src());
+			flow.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE), "2048");
+			flow.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV4_SRC), rule.getNw_src());
 
 			if (rule.getNw_dst() != null) {
-				flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, rule.getNw_dst());
+				flow.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV4_DST), rule.getNw_dst());
 			}
 			if (rule.getNw_proto() != 0) {
-				flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO,
+				flow.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.IP_PROTO),
 						Integer.toString(rule.getNw_proto()));
 			}
 			if (rule.getAction() == Action.ALLOW) {
-				flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS,
+				flow.put(StaticEntryPusher.Columns.COLUMN_ACTIONS,
 						"output=controller");
 			}
 			if (rule.getTp_dst() != 0) {
-				flow.put(StaticFlowEntryPusher.COLUMN_TP_DST,
+				flow.put(StaticEntryPusher.Columns.COLUMN_TP_DST,
 						Integer.toString(rule.getTp_dst()));
 			}
 
 			storageSource
-					.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow);
+					.insertRowAsync(StaticEntryPusher.TABLE_NAME, flow);
 
 		} else {
 
 			HashMap<String, Object> flow = new HashMap<String, Object>();
 
-			flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, dpid);
-			flow.put(StaticFlowEntryPusher.COLUMN_NAME, flowName);
-			flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE,
+			flow.put(StaticEntryPusher.Columns.COLUMN_SWITCH, dpid);
+			flow.put(StaticEntryPusher.Columns.COLUMN_NAME, flowName);
+			flow.put(StaticEntryPusher.Columns.COLUMN_ACTIVE,
 					Boolean.toString(true));
-			flow.put(StaticFlowEntryPusher.COLUMN_COOKIE, "0");
-			flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY,
+			flow.put(StaticEntryPusher.Columns.COLUMN_COOKIE, "0");
+			flow.put(StaticEntryPusher.Columns.COLUMN_PRIORITY,
 					Integer.toString(priority));
 
-			flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048");
-			flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, rule.getNw_dst());
+			flow.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE), "2048");
+			flow.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV4_DST), rule.getNw_dst());
 
 			if (rule.getNw_proto() != 0) {
-				flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO,
+				flow.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.IP_PROTO),
 						Integer.toString(rule.getNw_proto()));
 			}
 			if (rule.getAction() == Action.ALLOW) {
-				flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS,
+				flow.put(StaticEntryPusher.Columns.COLUMN_ACTIONS,
 						"output=controller");
 			}
 			if (rule.getTp_dst() != 0) {
-				flow.put(StaticFlowEntryPusher.COLUMN_TP_DST,
+				flow.put(StaticEntryPusher.Columns.COLUMN_TP_DST,
 						Integer.toString(rule.getTp_dst()));
 			}
 
 			storageSource
-					.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow);
+					.insertRowAsync(StaticEntryPusher.TABLE_NAME, flow);
 
 		}
 		addRuleToSwitchMapping(rule.getId(), dpid);
@@ -362,7 +363,7 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
 			return;
 		}
 
-		String dpid = HexString.toHexString(switchPort[0].getSwitchDPID()
+		String dpid = HexString.toHexString(switchPort[0].getNodeId()
 				.getLong());
 		String ip = IPv4.fromIPv4Address(ips[0].getInt());
 		logger.debug("AP(dpid:{},ip:{}) is added", dpid, ip);
@@ -428,7 +429,7 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
 		SwitchPort[] switchPort = device.getAttachmentPoints();
 		IPv4Address[] ips = device.getIPv4Addresses();
 
-		String dpid = HexString.toHexString(switchPort[0].getSwitchDPID()
+		String dpid = HexString.toHexString(switchPort[0].getNodeId()
 				.getLong());
 		String ip = null;
 		// some device may first appear with no IP address(default set to
diff --git a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
index 74043b84a824eff5683a6fb924a6da2010631dc5..04ed34e0322c22bd02610686093670ca08cdd3de 100644
--- a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
+++ b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
@@ -18,12 +18,10 @@
 package net.floodlightcontroller.core;
 
 import java.util.List;
-import java.util.Set;
 import java.util.Map;
 
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.packet.Ethernet;
-import io.netty.util.Timer;
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.HARole;
 import net.floodlightcontroller.core.IHAListener;
@@ -38,8 +36,7 @@ import net.floodlightcontroller.core.FloodlightContextStore;
 
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFType;
-import org.projectfloodlight.openflow.types.IPv4Address;
-import org.projectfloodlight.openflow.types.TransportPort;
+
 /**
  * The interface exposed by the core bundle that allows you to interact
  * with connected switches.
@@ -114,18 +111,6 @@ public interface IFloodlightProviderService extends
      */
     public String getControllerId();
 
-    /**
-     * Gets the controller addresses
-     * @return the controller addresses
-     */
-    public Set<IPv4Address> getOFAddresses();
-
-    /**
-     * Gets the controller's openflow port
-     * @return the controller's openflow port
-     */
-    public TransportPort getOFPort();
-
     /**
      * Set the role of the controller
      * @param role The new role for the controller node
@@ -204,22 +189,9 @@ public interface IFloodlightProviderService extends
     */
    public Long getUptime();
 
-   /**
-    * Get the set of port prefixes that will define an UPLINK port.
-    * @return The set of prefixes
-    */
-   public Set<String> getUplinkPortPrefixSet();
-
-
    public void handleMessage(IOFSwitch sw, OFMessage m,
                           FloodlightContext bContext);
 
-   /**
-    * Gets a hash wheeled timer to be used for for timeout scheduling
-    * @return a hash wheeled timer
-    */
-   public Timer getTimer();
-
    /**
     * Gets the role manager
     * @return the role manager
@@ -232,12 +204,6 @@ public interface IFloodlightProviderService extends
     */
    ModuleLoaderState getModuleLoaderState();
 
-   /**
-    * Gets the current number of worker threads
-    * @return Used for netty setup
-    */
-   public int getWorkerThreads();
-
    // paag
    /**
     * Add a completion listener to the controller
@@ -252,5 +218,4 @@ public interface IFloodlightProviderService extends
     * @param listener
     */
    void removeCompletionListener(IControllerCompletionListener listener);
-}
-
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/IReadyForReconcileListener.java b/src/main/java/net/floodlightcontroller/core/IReadyForReconcileListener.java
deleted file mode 100644
index 296347551e1d31ef02487ffd4760b66f9cbb99ca..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/core/IReadyForReconcileListener.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package net.floodlightcontroller.core;
-
-/**
- * This listener is a temporary solution to start flow reconciliation
- * after a Slave -> Master transition. It is fired once all known switches are
- * active (either because they all reconnected or because they did not
- * reconnect and have timed out).
- *
- * Modules should generally not rely on this notification unless there are
- * strong and compelling reasons to do so. I general modules should handle
- * the fact that some known switches are not active!
- * @author gregor
- *
- */
-public interface IReadyForReconcileListener {
-    public void readyForReconcile();
-}
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index 5bed0f022a645c3f3c4054802d5cbb8384c50e25..f3403c35b2422e66b2797294a8276fbbc2d79468 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -17,12 +17,12 @@
 
 package net.floodlightcontroller.core.internal;
 
+import java.io.IOException;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -36,8 +36,6 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.LinkedBlockingQueue;
 
-import io.netty.util.HashedWheelTimer;
-import io.netty.util.Timer;
 import net.floodlightcontroller.core.ControllerId;
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.HAListenerTypeMarker;
@@ -46,31 +44,17 @@ import net.floodlightcontroller.core.IControllerCompletionListener;
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IHAListener;
 import net.floodlightcontroller.core.IInfoProvider;
-import net.floodlightcontroller.core.IShutdownService;
 import net.floodlightcontroller.core.IListener.Command;
 import net.floodlightcontroller.core.IOFMessageListener;
 import net.floodlightcontroller.core.IOFSwitch;
-import net.floodlightcontroller.core.IOFSwitchListener;
+import net.floodlightcontroller.core.IShutdownService;
 import net.floodlightcontroller.core.LogicalOFMessageCategory;
-import net.floodlightcontroller.core.PortChangeType;
 import net.floodlightcontroller.core.RoleInfo;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.FloodlightModuleLoader;
 import net.floodlightcontroller.core.util.ListenerDispatcher;
 import net.floodlightcontroller.core.web.CoreWebRoutable;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.notification.INotificationManager;
-import net.floodlightcontroller.notification.NotificationManagerFactory;
-
-import org.projectfloodlight.openflow.protocol.OFMessage;
-import org.projectfloodlight.openflow.protocol.OFPacketIn;
-import org.projectfloodlight.openflow.protocol.OFPortDesc;
-import org.projectfloodlight.openflow.protocol.OFType;
-import org.projectfloodlight.openflow.types.DatapathId;
-import org.projectfloodlight.openflow.types.IPv4Address;
-import org.projectfloodlight.openflow.types.TransportPort;
-
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.perfmon.IPktInProcessingTimeService;
 import net.floodlightcontroller.restserver.IRestApiService;
@@ -79,14 +63,15 @@ import net.floodlightcontroller.storage.IStorageSourceListener;
 import net.floodlightcontroller.storage.IStorageSourceService;
 import net.floodlightcontroller.storage.StorageException;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
+import net.floodlightcontroller.util.LoadMonitor;
 
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPacketIn;
+import org.projectfloodlight.openflow.protocol.OFType;
 import org.sdnplatform.sync.ISyncService;
 import org.sdnplatform.sync.ISyncService.Scope;
 import org.sdnplatform.sync.error.SyncException;
 import org.sdnplatform.sync.internal.config.ClusterConfig;
-
-import net.floodlightcontroller.util.LoadMonitor;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -94,69 +79,63 @@ import com.google.common.base.Optional;
 import com.google.common.base.Strings;
 
 /**
- * The main controller class.  Handles all setup and network listeners
+ * The main controller class
  */
 public class Controller implements IFloodlightProviderService, IStorageSourceListener, IInfoProvider {
-
     protected static final Logger log = LoggerFactory.getLogger(Controller.class);
-    protected static final INotificationManager notifier = NotificationManagerFactory.getNotificationManager(Controller.class);
 
-    static final String ERROR_DATABASE = "The controller could not communicate with the system database.";
-
-    protected ConcurrentMap<OFType, ListenerDispatcher<OFType,IOFMessageListener>> messageListeners;
-    
-    // paag
-    protected ConcurrentLinkedQueue<IControllerCompletionListener> completionListeners;
+    /* OpenFlow message listeners and dispatchers */
+    protected static ConcurrentMap<OFType, ListenerDispatcher<OFType,IOFMessageListener>> messageListeners;
+    protected static ConcurrentLinkedQueue<IControllerCompletionListener> completionListeners;
     
-    // The controllerNodeIPsCache maps Controller IDs to their IP address.
-    // It's only used by handleControllerNodeIPsChanged
-    protected HashMap<String, String> controllerNodeIPsCache;
-
-    protected ListenerDispatcher<HAListenerTypeMarker,IHAListener> haListeners;
-    protected Map<String, List<IInfoProvider>> providerMap;
-    protected BlockingQueue<IUpdate> updates;
-    protected ControllerCounters counters;
-    protected Timer timer;
+    /* 
+     * The controllerNodeIPsCache maps Controller IDs to their IP address.
+     * It's only used by handleControllerNodeIPsChanged 
+     */
+    protected static HashMap<String, String> controllerNodeIPsCache;
+
+    protected static ListenerDispatcher<HAListenerTypeMarker,IHAListener> haListeners;
+    protected static Map<String, List<IInfoProvider>> providerMap;
+    protected static BlockingQueue<IUpdate> updates;
+    protected static ControllerCounters counters;
     
-    // Module Loader State
-    private ModuleLoaderState moduleLoaderState;
+    /* Module Loader State */
+    private static ModuleLoaderState moduleLoaderState;
     public enum ModuleLoaderState {
         INIT, STARTUP, COMPLETE
     }
 
-    // Module dependencies
-    private IStorageSourceService storageSourceService;
-    private IOFSwitchService switchService;
-    private IDebugCounterService debugCounterService;
-    protected IDebugEventService debugEventService;
-    private IRestApiService restApiService;
-    private IPktInProcessingTimeService pktinProcTimeService;
-    private IThreadPoolService threadPoolService;
-    private ISyncService syncService;
-    private IShutdownService shutdownService;
-
-    // Configuration options
-    private static TransportPort openFlowPort = TransportPort.of(6653); // new registered OF port number
-	private static Set<IPv4Address> openFlowAddresses = new HashSet<IPv4Address>();
-	public static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
-    protected int workerThreads = 16;
-    
-    // The id for this controller node. Should be unique for each controller
-    // node in a controller cluster.
+    /* Module dependencies */
+    private static IStorageSourceService storageSourceService;
+    private static IOFSwitchService switchService;
+    private static IDebugCounterService debugCounterService;
+    private static IRestApiService restApiService;
+    private static IPktInProcessingTimeService pktinProcTimeService;
+    private static IThreadPoolService threadPoolService;
+    private static ISyncService syncService;
+    private static IShutdownService shutdownService;
+
+    /*
+     * The id for this controller node. Should be unique for each controller
+     * node in a controller cluster.
+     */
     protected String controllerId = "my-floodlight-controller";
 
-    // This controller's current role that modules can use/query to decide
-    // if they should operate in ACTIVE / STANDBY
+    /*
+     *  This controller's current role that modules can use/query to decide
+     * if they should operate in ACTIVE / STANDBY
+     */
     protected volatile HARole notifiedRole;
 
     private static final String INITIAL_ROLE_CHANGE_DESCRIPTION = "Controller startup.";
-    /**
+    
+    /*
      * NOTE: roleManager is not 'final' because it's initialized at run time
      * based on parameters that are only available in init()
      */
-    private RoleManager roleManager;
+    private static RoleManager roleManager;
 
-    // Storage table names
+    /* Storage table names */
     protected static final String CONTROLLER_TABLE_NAME = "controller_controller";
     protected static final String CONTROLLER_ID = "id";
 
@@ -170,7 +149,6 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
     protected static final String CONTROLLER_INTERFACE_NUMBER = "number";
     protected static final String CONTROLLER_INTERFACE_DISCOVERED_IP = "discovered_ip";
 
-    // FIXME: don't use "forwardingconfig" as table name
     private static final String FLOW_PRIORITY_TABLE_NAME = "controller_forwardingconfig";
     private static final String FLOW_COLUMN_PRIMARY_KEY = "id";
     private static final String FLOW_VALUE_PRIMARY_KEY = "forwarding";
@@ -181,67 +159,17 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
             FLOW_COLUMN_ACCESS_PRIORITY,
             FLOW_COLUMN_CORE_PRIORITY
     };
-    protected static final boolean ALWAYS_DECODE_ETH = true;
-
-    // Set of port name prefixes that will be classified as uplink ports,
-    // hence will not be autoportfast.
-    Set<String> uplinkPortPrefixSet;
-
-    @Override
-    public Set<String> getUplinkPortPrefixSet() {
-        return uplinkPortPrefixSet;
-    }
-
-    public void setUplinkPortPrefixSet(Set<String> prefixSet) {
-        this.uplinkPortPrefixSet = prefixSet;
-    }
+    
+    protected static boolean alwaysDecodeEth = true;
 
     @Override
     public ModuleLoaderState getModuleLoaderState(){
-        return this.moduleLoaderState;
+        return moduleLoaderState;
     }
 
     // Load monitor for overload protection
     protected final boolean overload_drop = Boolean.parseBoolean(System.getProperty("overload_drop", "false"));
     protected final LoadMonitor loadmonitor = new LoadMonitor(log);
-    
-    private static class NotificationSwitchListener implements IOFSwitchListener {
-
-        @Override
-        public void switchAdded(DatapathId switchId) {
-            notifier.postNotification("Switch " +  switchId + " connected.");
-        }
-
-        @Override
-        public void switchRemoved(DatapathId switchId) {
-            notifier.postNotification("Switch " + switchId + " disconnected.");
-        }
-
-        @Override
-        public void switchActivated(DatapathId switchId) {
-
-        }
-
-        @Override
-        public void switchPortChanged(DatapathId switchId, OFPortDesc port,
-                                      PortChangeType type) {
-            String msg = String.format("Switch %s port %s changed: %s",
-                                       switchId,
-                                       port.getName(),
-                                       type.toString());
-            notifier.postNotification(msg);
-        }
-
-        @Override
-        public void switchChanged(DatapathId switchId) {
-        }
-
-		@Override
-		public void switchDeactivated(DatapathId switchId) {
-			// TODO Auto-generated method stub
-			
-		}
-    }
                 
     /**
      *  Updates handled by the main loop
@@ -292,63 +220,54 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
     // ***************
 
     void setStorageSourceService(IStorageSourceService storageSource) {
-        this.storageSourceService = storageSource;
+        storageSourceService = storageSource;
     }
 
     IStorageSourceService getStorageSourceService() {
-        return this.storageSourceService;
+        return storageSourceService;
     }
     
     IShutdownService getShutdownService() {
-    	return this.shutdownService;
+    	return shutdownService;
     }
     
     void setShutdownService(IShutdownService shutdownService) {
-    	this.shutdownService = shutdownService;
-    }
-
-    public void setDebugEvent(IDebugEventService debugEvent) {
-        this.debugEventService = debugEvent;
+    	Controller.shutdownService = shutdownService;
     }
     
     void setDebugCounter(IDebugCounterService debugCounters) {
-        this.debugCounterService = debugCounters;
+        debugCounterService = debugCounters;
     }
 
     IDebugCounterService getDebugCounter() {
-        return this.debugCounterService;
+        return debugCounterService;
     }
 
     void setSyncService(ISyncService syncService) {
-        this.syncService = syncService;
+        Controller.syncService = syncService;
     }
     void setPktInProcessingService(IPktInProcessingTimeService pits) {
-        this.pktinProcTimeService = pits;
+        Controller.pktinProcTimeService = pits;
     }
     
     void setRestApiService(IRestApiService restApi) {
-        this.restApiService = restApi;
+        Controller.restApiService = restApi;
     }
 
     
     void setThreadPoolService(IThreadPoolService tp) {
-        this.threadPoolService = tp;
+        threadPoolService = tp;
     }
     IThreadPoolService getThreadPoolService() {
-        return this.threadPoolService;
+        return threadPoolService;
     }
 
     
     public void setSwitchService(IOFSwitchService switchService) {
-       this.switchService = switchService;
+       Controller.switchService = switchService;
     }
     public IOFSwitchService getSwitchService() {
-        return this.switchService;
-    }
-
-    @Override
-    public int getWorkerThreads() {
-        return this.workerThreads;
+        return Controller.switchService;
     }
 
     @Override
@@ -447,7 +366,7 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
                     return;
                 }
 
-                if (Controller.ALWAYS_DECODE_ETH) {
+                if (alwaysDecodeEth) {
                     eth = new Ethernet();
                     eth.deserialize(pi.getData(), 0, pi.getData().length);
                 }
@@ -492,16 +411,11 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
                         }
                     }
                     pktinProcTimeService.recordEndTimePktIn(sw, m, bc);
-                } else {
-                    if (m.getType() != OFType.BARRIER_REPLY)
-                        log.warn("Unhandled OF Message: {} from {}", m, sw);
-                    else
-                        log.debug("Received a Barrier Reply, no listeners for it");
                 }
                 // paag
                 // And just before we exit the controller loop we see if anyone
                 // is interested in knowing that we are exiting the loop
-                for (IControllerCompletionListener listener:completionListeners)
+                for (IControllerCompletionListener listener : completionListeners)
                 	listener.onMessageConsumed(sw, m, bc);
                 
                 if ((bContext == null) && (bc != null)) flcontext_free(bc);
@@ -525,24 +439,12 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
     public String getControllerId() {
         return controllerId;
     }
-    
-    @Override
-    public Set<IPv4Address> getOFAddresses() {
-        return Collections.unmodifiableSet(openFlowAddresses);
-    }
-    
-    @Override
-    public TransportPort getOFPort() {
-        return openFlowPort;
-    }
 
-    // paag
     @Override
     public synchronized void addCompletionListener(IControllerCompletionListener listener) {
     	completionListeners.add(listener);
     }
     
-    //paag
     @Override
     public synchronized void removeCompletionListener(IControllerCompletionListener listener) {
     	String listenerName = listener.getName();
@@ -682,7 +584,7 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
      */
     @Override
     public void run() {
-        this.moduleLoaderState = ModuleLoaderState.COMPLETE;
+        moduleLoaderState = ModuleLoaderState.COMPLETE;
 
         if (log.isDebugEnabled()) {
             logListeners();
@@ -709,51 +611,30 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
     }
     
     private void setConfigParams(Map<String, String> configParams) throws FloodlightModuleException {
-        String ofPort = configParams.get("openFlowPort");
-        if (!Strings.isNullOrEmpty(ofPort)) {
-            try {
-                openFlowPort = TransportPort.of(Integer.parseInt(ofPort));
-            } catch (Exception e) {
-                log.error("Invalid OpenFlow port {}, {}", ofPort, e);
-                throw new FloodlightModuleException("Invalid OpenFlow port of " + ofPort + " in config");
-            }
-        }
-        log.info("OpenFlow port set to {}", openFlowPort);
-
-        String threads = configParams.get("workerThreads");
-        if (!Strings.isNullOrEmpty(threads)) {
-            this.workerThreads = Integer.parseInt(threads);
-        }
-        log.info("Number of worker threads set to {}", this.workerThreads);
-        
-        
         /**
          * Tulio Ribeiro
          */
         String controllerId = configParams.get("controllerId");
         if (!Strings.isNullOrEmpty(controllerId)) {
-            try {
-            	this.controllerId = controllerId;
-            } catch (Exception e) {
-                log.error("Invalid controlelrId {}, {}", controllerId, e);
-                throw new FloodlightModuleException("Invalid controllerId of " + controllerId + " in config");
-            }
+            this.controllerId = controllerId;
         }        
         log.info("ControllerId set to {}", this.controllerId);
         
-        
-        
-        String addresses = configParams.get("openFlowAddresses");
-        if (!Strings.isNullOrEmpty(addresses)) {
-            try {
-                openFlowAddresses = Collections.singleton(IPv4Address.of(addresses)); //TODO support list of addresses for multi-honed controllers
-            } catch (Exception e) {
-                log.error("Invalid OpenFlow address {}, {}", addresses, e);
-                throw new FloodlightModuleException("Invalid OpenFlow address of " + addresses + " in config");
-            }
-            log.info("OpenFlow addresses set to {}", openFlowAddresses);
+        String decodeEth = configParams.get("deserializeEthPacketIns");
+        if (!Strings.isNullOrEmpty(decodeEth)) {
+        	try {
+        		alwaysDecodeEth = Boolean.parseBoolean(decodeEth);
+        	} catch (Exception e) {
+        		log.error("Could not parse 'deserializeEthPacketIns' of {}. Using default setting of {}", decodeEth, alwaysDecodeEth);
+        	}
+        }        
+        if (alwaysDecodeEth) {
+	        log.warn("Controller will automatically deserialize all Ethernet packet-in messages. "
+	        		+ "Set 'deserializeEthPacketIns' to 'FALSE' if this feature is not "
+	        		+ "required or when benchmarking core performance");
         } else {
-        	openFlowAddresses.add(IPv4Address.NONE);
+            log.info("Controller will not automatically deserialize all Ethernet packet-in messages. "
+            		+ "Set 'deserializeEthPacketIns' to 'TRUE' to enable this feature");
         }
     }
 
@@ -762,33 +643,29 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
      */
     public void init(Map<String, String> configParams) throws FloodlightModuleException {
 
-        this.moduleLoaderState = ModuleLoaderState.INIT;
+        moduleLoaderState = ModuleLoaderState.INIT;
 
         // These data structures are initialized here because other
         // module's startUp() might be called before ours        
-        this.messageListeners = new ConcurrentHashMap<OFType, ListenerDispatcher<OFType, IOFMessageListener>>();
-        this.haListeners = new ListenerDispatcher<HAListenerTypeMarker, IHAListener>();
-        this.controllerNodeIPsCache = new HashMap<String, String>();
-        this.updates = new LinkedBlockingQueue<IUpdate>();
-        this.providerMap = new HashMap<String, List<IInfoProvider>>();
-        this.completionListeners = new ConcurrentLinkedQueue<IControllerCompletionListener>();
+        messageListeners = new ConcurrentHashMap<OFType, ListenerDispatcher<OFType, IOFMessageListener>>();
+        haListeners = new ListenerDispatcher<HAListenerTypeMarker, IHAListener>();
+        controllerNodeIPsCache = new HashMap<String, String>();
+        updates = new LinkedBlockingQueue<IUpdate>();
+        providerMap = new HashMap<String, List<IInfoProvider>>();
+        completionListeners = new ConcurrentLinkedQueue<IControllerCompletionListener>();
         
         setConfigParams(configParams);
 
         HARole initialRole = getInitialRole(configParams);
-        this.notifiedRole = initialRole;
-        this.shutdownService = new ShutdownServiceImpl();
+        notifiedRole = initialRole;
+        shutdownService = new ShutdownServiceImpl();
 
-        this.roleManager = new RoleManager(this, this.shutdownService,
-                                           this.notifiedRole,
+        roleManager = new RoleManager(this, shutdownService,
+                                           notifiedRole,
                                            INITIAL_ROLE_CHANGE_DESCRIPTION);
-        this.timer = new HashedWheelTimer();
-
         // Switch Service Startup
-        this.switchService.registerLogicalOFMessageCategory(LogicalOFMessageCategory.MAIN);
-        this.switchService.addOFSwitchListener(new NotificationSwitchListener());
-
-        this.counters = new ControllerCounters(debugCounterService);
+        switchService.registerLogicalOFMessageCategory(LogicalOFMessageCategory.MAIN);
+        counters = new ControllerCounters(debugCounterService);
      }
 
     /**
@@ -797,7 +674,7 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
      */
     public void startupComponents(FloodlightModuleLoader floodlightModuleLoader) throws FloodlightModuleException {
 
-        this.moduleLoaderState = ModuleLoaderState.STARTUP;
+        moduleLoaderState = ModuleLoaderState.STARTUP;
 
         // Create the table names we use
         storageSourceService.createTable(CONTROLLER_TABLE_NAME, null);
@@ -813,14 +690,14 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
         
         // Startup load monitoring
         if (overload_drop) {
-            this.loadmonitor.startMonitoring(this.threadPoolService.getScheduledExecutor());
+            this.loadmonitor.startMonitoring(threadPoolService.getScheduledExecutor());
         }
 
         // Add our REST API
         restApiService.addRestletRoutable(new CoreWebRoutable());
                 
         try {
-            this.syncService.registerStore(OFSwitchManager.SWITCH_SYNC_STORE_NAME, Scope.LOCAL);
+            syncService.registerStore(OFSwitchManager.SWITCH_SYNC_STORE_NAME, Scope.LOCAL);
         } catch (SyncException e) {
             throw new FloodlightModuleException("Error while setting up sync service", e);
         }
@@ -891,12 +768,12 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
 
     @Override
     public void addHAListener(IHAListener listener) {
-        this.haListeners.addListener(null,listener);
+        haListeners.addListener(null,listener);
     }
 
     @Override
     public void removeHAListener(IHAListener listener) {
-        this.haListeners.removeListener(listener);
+        haListeners.removeListener(listener);
     }
 
     /**
@@ -1007,7 +884,7 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
         m.put("free", runtime.freeMemory());
         return m;
     }
-
+    
     @Override
     public Long getUptime() {
         RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
@@ -1017,7 +894,7 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
     @Override
     public void addUpdateToQueue(IUpdate update) {
         try {
-            this.updates.put(update);
+            updates.put(update);
         } catch (InterruptedException e) {
             // This should never happen
             log.error("Failure adding update {} to queue.", update);
@@ -1029,7 +906,7 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
      * Dispatch all updates in the update queue until queue is empty
      */
     void processUpdateQueueForTesting() {
-        while(!updates.isEmpty()) {
+        while (!updates.isEmpty()) {
             IUpdate update = updates.poll();
             if (update != null)
                 update.dispatch();
@@ -1041,14 +918,14 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
      * check if update queue is empty
      */
     boolean isUpdateQueueEmptyForTesting() {
-        return this.updates.isEmpty();
+        return updates.isEmpty();
     }
 
     /**
      * FOR TESTING ONLY
      */
     void resetModuleState() {
-        this.moduleLoaderState = ModuleLoaderState.INIT;
+        moduleLoaderState = ModuleLoaderState.INIT;
     }
 
     /**
@@ -1056,7 +933,7 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
      * sets module loader state
      */
     void setModuleLoaderStateForTesting(ModuleLoaderState state) {
-        this.moduleLoaderState = state;
+        moduleLoaderState = state;
     }
 
     @Override
@@ -1065,7 +942,7 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
 
         Map<String, Object> info = new HashMap<String, Object>();
 
-        info.put("# Switches", this.switchService.getAllSwitchDpids().size());
+        info.put("# Switches", switchService.getAllSwitchDpids().size());
         return info;
     }
 
@@ -1075,25 +952,18 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
 
     @Override
     public RoleManager getRoleManager() {
-        return this.roleManager;
+        return roleManager;
     }
 
     public Optional<ControllerId> getId() {
-        short nodeId = this.syncService.getLocalNodeId();
+        short nodeId = syncService.getLocalNodeId();
         if(nodeId == ClusterConfig.NODE_ID_UNCONFIGURED)
             return Optional.absent();
         else
             return Optional.of(ControllerId.of(nodeId));
     }
-
-    @Override
-    public Timer getTimer() {
-        return this.timer;
-    }
-
+    
     public ControllerCounters getCounters() {
-        return this.counters;
+        return counters;
     }
-}
-
-
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/internal/FloodlightProvider.java b/src/main/java/net/floodlightcontroller/core/internal/FloodlightProvider.java
index e28d428a2a2cb210600a3ea700bdb112648c70f6..330391a8c14c3e6193bc89ba16ac222be5412032 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/FloodlightProvider.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/FloodlightProvider.java
@@ -21,24 +21,20 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
-import net.floodlightcontroller.core.internal.IOFSwitchService;
-import net.floodlightcontroller.core.internal.Controller;
-import net.floodlightcontroller.core.module.Run;
-
-import org.sdnplatform.sync.ISyncService;
-
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.core.module.Run;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
 import net.floodlightcontroller.perfmon.IPktInProcessingTimeService;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.storage.IStorageSourceService;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
 
+import org.sdnplatform.sync.ISyncService;
+
 public class FloodlightProvider implements IFloodlightModule {
     Controller controller;
 
@@ -75,7 +71,6 @@ public class FloodlightProvider implements IFloodlightModule {
         dependencies.add(IPktInProcessingTimeService.class);
         dependencies.add(IRestApiService.class);
         dependencies.add(IDebugCounterService.class);
-        dependencies.add(IDebugEventService.class);
         dependencies.add(IOFSwitchService.class);
         dependencies.add(IThreadPoolService.class);
         dependencies.add(ISyncService.class);
@@ -90,8 +85,6 @@ public class FloodlightProvider implements IFloodlightModule {
            context.getServiceImpl(IPktInProcessingTimeService.class));
        controller.setDebugCounter(
            context.getServiceImpl(IDebugCounterService.class));
-       controller.setDebugEvent(
-           context.getServiceImpl(IDebugEventService.class));
        controller.setRestApiService(
            context.getServiceImpl(IRestApiService.class));
        controller.setThreadPoolService(
diff --git a/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchService.java b/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchService.java
index 24e105ed06a96824231e3e1de5aeee5be6010672..431ac877cf6b95fcf272e5250dfcb4a6f8fcea98 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchService.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchService.java
@@ -9,7 +9,6 @@ import net.floodlightcontroller.core.IOFSwitchDriver;
 import net.floodlightcontroller.core.IOFSwitchListener;
 import net.floodlightcontroller.core.LogicalOFMessageCategory;
 import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.core.rest.SwitchRepresentation;
 
 import org.projectfloodlight.openflow.types.DatapathId;
 
@@ -85,19 +84,6 @@ public interface IOFSwitchService extends IFloodlightService {
      */
     void registerHandshakePlugin(IAppHandshakePluginFactory plugin);
 
-    /**
-     * Get the REST representations of the active switches.
-     * @return Representation wrappers of the active switches.
-     */
-    List<SwitchRepresentation> getSwitchRepresentations();
-
-    /**
-     * Get the REST representation of a switch.
-     * @param dpid the dpid of the desired switch representation.
-     * @return The switch representation.
-     */
-    SwitchRepresentation getSwitchRepresentation(DatapathId dpid);
-
     /**
      * Returns a snapshot of the set DPIDs for all known switches.
      *
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
index 41b63c45d5eb2cf10a8c65a695321de71a7b2708..79f953e1391945e37e057d90afdfa8114d0028fa 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
@@ -39,11 +39,8 @@ import org.projectfloodlight.openflow.protocol.OFHelloElem;
 import org.projectfloodlight.openflow.protocol.OFHelloElemVersionbitmap;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
-import org.projectfloodlight.openflow.protocol.OFRoleStatus;
 import org.projectfloodlight.openflow.protocol.OFType;
 import org.projectfloodlight.openflow.protocol.OFVersion;
-import org.projectfloodlight.openflow.protocol.ver13.OFHelloElemTypeSerializerVer13;
-import org.projectfloodlight.openflow.protocol.ver14.OFHelloElemTypeSerializerVer14;
 import org.projectfloodlight.openflow.types.OFAuxId;
 import org.projectfloodlight.openflow.types.U32;
 import org.projectfloodlight.openflow.types.U64;
@@ -322,12 +319,10 @@ class OFChannelHandler extends SimpleChannelInboundHandler<Iterable<OFMessage>>
 				List<OFHelloElem> elements = m.getElements();
 				/* Grab all bitmaps supplied */
 				for (OFHelloElem e : elements) {
-					if (m.getVersion().equals(OFVersion.OF_13) 
-							&& e.getType() == OFHelloElemTypeSerializerVer13.VERSIONBITMAP_VAL) {
-						bitmaps.addAll(((OFHelloElemVersionbitmap) e).getBitmaps());
-					} else if (m.getVersion().equals(OFVersion.OF_14) 
-							&& e.getType() == OFHelloElemTypeSerializerVer14.VERSIONBITMAP_VAL) {
+					if (e instanceof OFHelloElemVersionbitmap) {
 						bitmaps.addAll(((OFHelloElemVersionbitmap) e).getBitmaps());
+					} else {
+						log.warn("Unhandled OFHelloElem {}", e);
 					}
 				}
 				/* Lookup highest, common supported OpenFlow version */
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFConnectionCounters.java b/src/main/java/net/floodlightcontroller/core/internal/OFConnectionCounters.java
index b92ab9ed71a031376f0805b0723c55ea6dcd7570..6e5dcbf38351bad9e38aaf3aff3d9c677fd4ea83 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFConnectionCounters.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFConnectionCounters.java
@@ -60,6 +60,7 @@ public class OFConnectionCounters {
     private final IDebugCounter ctrWriteRequestForward;
     private final IDebugCounter ctrWriteBundleControl;
     private final IDebugCounter ctrWriteBundleAdd;
+    private final IDebugCounter ctrWriteControllerStatus;
 
     // Read Counters
     //
@@ -98,6 +99,7 @@ public class OFConnectionCounters {
     private final IDebugCounter ctrReadBundleAdd;
     private final IDebugCounter ctrReadBundleControl;
     private final IDebugCounter ctrReadRequestForward;
+    private final IDebugCounter ctrReadControllerStatus;
 
     private static final Logger logger =
             LoggerFactory.getLogger(OFConnectionCounters.class);
@@ -326,6 +328,12 @@ public class OFConnectionCounters {
                                      hierarchy,
                                      stringId,
                                      OFType.TABLE_STATUS.toString());
+        
+        ctrWriteControllerStatus =
+        		registerCounterLocal(counters,
+                hierarchy,
+                stringId,
+                OFType.CONTROLLER_STATUS.toString());
 
         // Register Read Counters
         //
@@ -515,6 +523,12 @@ public class OFConnectionCounters {
                                      hierarchy,
                                      stringId,
                                      OFType.TABLE_STATUS.toString());
+        
+        ctrReadControllerStatus =
+        		registerCounterLocal(counters,
+                hierarchy,
+                stringId,
+                OFType.CONTROLLER_STATUS.toString());
     }
     
     /**
@@ -673,10 +687,15 @@ public class OFConnectionCounters {
                 ctrWriteTableStatus.increment();
                 break;
                 
+            case CONTROLLER_STATUS:
+            	ctrWriteControllerStatus.increment();
+    			break;   
+    			
             default:
                 logger.warn(ofm.getType().toString() +
                             ": Invalid OpenFlow Messaqe!");
                 break;
+		
          }
     }
 
@@ -826,6 +845,10 @@ public class OFConnectionCounters {
                ctrReadTableStatus.increment();
                break;
 
+           case CONTROLLER_STATUS:
+           		ctrReadControllerStatus.increment();
+           		break;   
+               
            default:
                logger.warn(ofm.getType().toString() +
                            ": Invalid OpenFlow Messaqe!");
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
index 1d3579d468d747714d10e3742a178a6f83d42363..32ded658a88916188881eef1711bb145a806db95 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
@@ -43,6 +43,7 @@ import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
 import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
 import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
+import org.projectfloodlight.openflow.protocol.OFGroupBucket;
 import org.projectfloodlight.openflow.protocol.OFGroupDelete;
 import org.projectfloodlight.openflow.protocol.OFGroupType;
 import org.projectfloodlight.openflow.protocol.OFMessage;
@@ -337,24 +338,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 					switchManagerCounters.roleReplyErrorUnsupported.increment();
 					setSwitchRole(pendingRole, RoleRecvStatus.UNSUPPORTED);
 				} else {
-					// TODO: Is this the right thing to do if we receive
-					// some other error besides a bad request error?
-					// Presumably that means the switch did actually
-					// understand the role request message, but there
-					// was some other error from processing the message.
-					// OF 1.2 specifies a ROLE_REQUEST_FAILED
-					// error code, but it doesn't look like the Nicira
-					// role request has that. Should check OVS source
-					// code to see if it's possible for any other errors
-					// to be returned.
-					// If we received an error the switch is not
-					// in the correct role, so we need to disconnect it.
-					// We could also resend the request but then we need to
-					// check if there are other pending request in which
-					// case we shouldn't resend. If we do resend we need
-					// to make sure that the switch eventually accepts one
-					// of our requests or disconnect the switch. This feels
-					// cumbersome.
 					String msg = String.format("Switch: [%s], State: [%s], "
 							+ "Unexpected error %s in respone to our "
 							+ "role request for %s.",
@@ -449,23 +432,19 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 			 * Clear all groups.
 			 * We have to do this for all types manually as of Loxi 0.9.0.
 			 */
-			OFGroupDelete delgroup = this.sw.getOFFactory().buildGroupDelete()
+			OFGroupDelete.Builder delgroup = this.sw.getOFFactory().buildGroupDelete()
 					.setGroup(OFGroup.ALL)
-					.setGroupType(OFGroupType.ALL)
-					.build();
-			this.sw.write(delgroup);
-			delgroup.createBuilder()
-			.setGroupType(OFGroupType.FF)
-			.build();
-			this.sw.write(delgroup);
-			delgroup.createBuilder()
-			.setGroupType(OFGroupType.INDIRECT)
-			.build();
-			this.sw.write(delgroup);
-			delgroup.createBuilder()
-			.setGroupType(OFGroupType.SELECT)
-			.build();
-			this.sw.write(delgroup);
+					.setGroupType(OFGroupType.ALL);
+			if (this.sw.getOFFactory().getVersion().compareTo(OFVersion.OF_15) >= 0) {
+				delgroup.setCommandBucketId(OFGroupBucket.BUCKET_ALL);
+			}
+			this.sw.write(delgroup.build());
+			delgroup.setGroupType(OFGroupType.FF);
+			this.sw.write(delgroup.build());
+			delgroup.setGroupType(OFGroupType.INDIRECT);
+			this.sw.write(delgroup.build());
+			delgroup.setGroupType(OFGroupType.SELECT);
+			this.sw.write(delgroup.build());
 
 			/*
 			 * Make sure we allow these operations to complete before proceeding.
@@ -574,9 +553,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 		}
 
 		void processOFGetConfigReply(OFGetConfigReply m) {
-			// we only expect config replies in the WAIT_CONFIG_REPLY state
-			// TODO: might use two different strategies depending on whether
-			// we got a miss length of 64k or not.
 			illegalMessageReceived(m);
 		}
 
@@ -815,7 +791,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 				throw new SwitchStateException(msg);
 			}
 			sw.processOFTableFeatures(replies);
-			//TODO like port status, might want to create an event and dispatch it. Not sure how useful this would be though...
 		}
 
 
@@ -957,9 +932,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 						+ "miss length set to 0xffff",
 						getSwitchInfoString());
 			} else {
-				// FIXME: we can't really deal with switches that don't send
-				// full packets. Shouldn't we drop the connection here?
-				// FIXME: count??
 				log.warn("Config Reply from switch {} has"
 						+ "miss length set to {}",
 						getSwitchInfoString(),
@@ -1132,7 +1104,8 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 
 		@Override
 		void enterState() {
-			if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) < 0) {
+			if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) < 0
+					|| sw.getOFFactory().getVersion().compareTo(OFVersion.OF_15) == 0) { //FIXME must skip for OVS
 				nextState();
 			} else {
 				sendHandshakeTableFeaturesRequest();
@@ -1149,7 +1122,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 
 		@Override
 		void processOFMessage(OFMessage m) {
-			// FIXME: other message to handle here?
 			sw.processDriverHandshakeMessage(m);
 			if (sw.isDriverHandshakeComplete()) {
 				setState(new WaitAppHandshakeState());
@@ -1404,10 +1376,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 				// a permission error. This is a likely indicator that
 				// the switch thinks we are slave. Reassert our
 				// role
-				// FIXME: this could be really bad during role transitions
-				// if two controllers are master (even if its only for
-				// a brief period). We might need to see if these errors
-				// persist before we reassert
 				switchManagerCounters.epermErrorWhileSwitchIsMaster.increment();
 				log.warn("Received permission error from switch {} while" +
 						"being master. Reasserting master role.",
@@ -1456,12 +1424,12 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 			 *  };
 			 */
 			//log.info("Processing roleStatus from MasterState...");
-			long role = m.getRole();
-			if(role==3)
+			OFControllerRole role = m.getRole();
+			if (role == OFControllerRole.ROLE_SLAVE)
 				sendRoleRequest(OFControllerRole.ROLE_SLAVE);
-			else if (role==2)
+			else if (role == OFControllerRole.ROLE_MASTER)
 				sendRoleRequest(OFControllerRole.ROLE_MASTER);
-			else if (role==1)
+			else if (role == OFControllerRole.ROLE_EQUAL)
 				sendRoleRequest(OFControllerRole.ROLE_EQUAL);
 			else
 				sendRoleRequest(OFControllerRole.ROLE_NOCHANGE);
@@ -1517,7 +1485,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 
 		@Override
 		void processOFStatsReply(OFStatsReply m) {
-			// TODO Auto-generated method stub
 			super.processOFStatsReply(m);
 		}
 	}
@@ -1597,12 +1564,12 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 			 *  OFPCR_ROLE_SLAVE = 		3, Read-only access. 
 			 *  };
 			 */
-			long role = m.getRole();
-			if(role==3)
+			OFControllerRole role = m.getRole();
+			if(role == OFControllerRole.ROLE_SLAVE)
 				sendRoleRequest(OFControllerRole.ROLE_SLAVE);
-			else if (role==2)
+			else if (role == OFControllerRole.ROLE_MASTER)
 				sendRoleRequest(OFControllerRole.ROLE_MASTER);
-			else if (role==1)
+			else if (role == OFControllerRole.ROLE_EQUAL)
 				sendRoleRequest(OFControllerRole.ROLE_EQUAL);
 			else
 				sendRoleRequest(OFControllerRole.ROLE_NOCHANGE);
@@ -1819,7 +1786,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 	 */
 	private void sendHandshakeSetConfig() {
 		// Ensure we receive the full packet via PacketIn
-		// FIXME: We don't set the reassembly flags.
 		OFSetConfig configSet = factory.buildSetConfig()
 				.setXid(handshakeTransactionIds--)
 				.setMissSendLen(0xffff)
@@ -1839,7 +1805,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 	}
 
 	protected void sendPortDescRequest() {
-		mainConnection.write(factory.portDescStatsRequest(ImmutableSet.<OFStatsRequestFlags>of()));
+		mainConnection.write(factory.buildPortDescStatsRequest().setFlags(ImmutableSet.<OFStatsRequestFlags>of()).build());
 	}
 
 	/**
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java
index 81767b89ccc6313a33fa314f9d942f755b42fe83..c4ce3faf596107c280ba026413c288a4326dae66 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java
@@ -39,12 +39,7 @@ import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.core.rest.SwitchRepresentation;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventType;
-import net.floodlightcontroller.debugevent.IEventCategory;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 
 import org.projectfloodlight.openflow.protocol.OFControllerRole;
 import org.projectfloodlight.openflow.protocol.OFFactories;
@@ -52,11 +47,13 @@ import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortState;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.IPv4Address;
 import org.projectfloodlight.openflow.types.OFAuxId;
 import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.TransportPort;
 import org.projectfloodlight.openflow.types.U32;
 import org.sdnplatform.sync.IStoreClient;
 import org.sdnplatform.sync.IStoreListener;
@@ -71,6 +68,7 @@ import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonToken;
 import com.fasterxml.jackson.databind.MappingJsonFactory;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -80,6 +78,8 @@ import io.netty.channel.ChannelOption;
 import io.netty.channel.group.DefaultChannelGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
 import io.netty.util.concurrent.GlobalEventExecutor;
 
 /**
@@ -89,1154 +89,1196 @@ import io.netty.util.concurrent.GlobalEventExecutor;
  * Switch Manager also provides the switch service, which allows other modules
  * to hook in switch listeners and get basic access to switch information.
  *
- * @author gregor, capveg, sovietaced
+ * @author gregor, capveg, sovietaced, rizard
  *
  */
 public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListener, 
 IHAListener, IFloodlightModule, IOFSwitchService, IStoreListener<DatapathId> {
-	private static final Logger log = LoggerFactory.getLogger(OFSwitchManager.class);
-
-	private volatile OFControllerRole role;
-	private SwitchManagerCounters counters;
-
-	private ISyncService syncService;
-	private IStoreClient<DatapathId, SwitchSyncRepresentation> storeClient;
-	public static final String SWITCH_SYNC_STORE_NAME = OFSwitchManager.class.getCanonicalName() + ".stateStore";
-
-	private static String keyStorePassword;
-	private static String keyStore;
-
-	protected static boolean clearTablesOnInitialConnectAsMaster = false;
-	protected static boolean clearTablesOnEachTransitionToMaster = false;
-
-	protected static Map<DatapathId, TableId> forwardToControllerFlowsUpToTableByDpid;
-	protected static TableId forwardToControllerFlowsUpToTable = TableId.of(4); /* this should cover most HW switches that have a couple SW-based flow tables */
-
-	protected static List<U32> ofBitmaps;
-	protected static OFFactory defaultFactory;
-
-	private ConcurrentHashMap<DatapathId, OFSwitchHandshakeHandler> switchHandlers;
-	private ConcurrentHashMap<DatapathId, IOFSwitchBackend> switches;
-	private ConcurrentHashMap<DatapathId, IOFSwitch> syncedSwitches;
-	
-	protected static Map<DatapathId, OFControllerRole> switchInitialRole;
-
-	private ISwitchDriverRegistry driverRegistry;
-
-	private Set<LogicalOFMessageCategory> logicalOFMessageCategories = new CopyOnWriteArraySet<LogicalOFMessageCategory>();
-	private final List<IAppHandshakePluginFactory> handshakePlugins = new CopyOnWriteArrayList<IAppHandshakePluginFactory>();
-	private int numRequiredConnections = -1;
-	// Event IDs for debug events
-	protected IEventCategory<SwitchEvent> evSwitch;
-
-	// ISwitchService
-	protected Set<IOFSwitchListener> switchListeners;
-
-	// Module Dependencies
-	private IFloodlightProviderService floodlightProvider;
-	private IDebugEventService debugEventService;
-	private IDebugCounterService debugCounterService;
-
-	private NioEventLoopGroup bossGroup;
-	private NioEventLoopGroup workerGroup;
-	private DefaultChannelGroup cg;
-
-	/** IHAListener Implementation **/
-	@Override
-	public void transitionToActive() {
-		this.role = HARole.ACTIVE.getOFRole();
-	}
-
-	@Override
-	public void transitionToStandby() {
-		this.role = HARole.STANDBY.getOFRole();
-	}
-
-	/** IOFSwitchManager Implementation **/
-
-	@Override public SwitchManagerCounters getCounters() {
-		return this.counters;
-	}
-
-	private void addUpdateToQueue(IUpdate iUpdate) {
-		floodlightProvider.addUpdateToQueue(iUpdate);
-	}
-
-	@Override
-	public synchronized void switchAdded(IOFSwitchBackend sw) {
-		DatapathId dpid = sw.getId();
-		IOFSwitchBackend oldSw = this.switches.put(dpid, sw);
-		// Update event history
-		evSwitch.newEventWithFlush(new SwitchEvent(dpid, "connected"));
-
-		if (oldSw == sw)  {
-			// Note == for object equality, not .equals for value
-			counters.errorActivatedSwitchNotPresent.increment();
-			log.error("Switch {} added twice?", sw);
-			return;
-		} else if (oldSw != null) {
-			// This happens either when we have switches with duplicate
-			// DPIDs or when a switch reconnects before we saw the
-			// disconnect
-			counters.switchWithSameDpidActivated.increment();
-			log.warn("New switch added {} for already-added switch {}", sw, oldSw);
-			// We need to disconnect and remove the old switch
-			// TODO: we notify switch listeners that the switch has been
-			// removed and then we notify them that the new one has been
-			// added. One could argue that a switchChanged notification
-			// might be more appropriate in this case....
-			oldSw.cancelAllPendingRequests();
-			addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED));
-			oldSw.disconnect();
-		}
-
-		/*
-		 * Set some other config options for this switch.
-		 */
-		if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
-			if (forwardToControllerFlowsUpToTableByDpid.containsKey(sw.getId())) {
-				sw.setMaxTableForTableMissFlow(forwardToControllerFlowsUpToTableByDpid.get(sw.getId()));
-			} else {
-				sw.setMaxTableForTableMissFlow(forwardToControllerFlowsUpToTable);
-			}
-		}
-	}
-
-	@Override
-	public synchronized void switchStatusChanged(IOFSwitchBackend sw, SwitchStatus oldStatus, SwitchStatus newStatus) {
-		DatapathId dpid = sw.getId();
-		IOFSwitchBackend presentSw = this.switches.get(dpid);
-
-		if (presentSw != sw)  {
-			// Note == for object equality, not .equals for value
-			counters.errorActivatedSwitchNotPresent
-			.increment();
-			log.debug("Switch {} status change but not present in sync manager", sw);
-			return;
-		}
-		evSwitch.newEventWithFlush(new SwitchEvent(dpid,
-				String.format("%s -> %s",
-						oldStatus,
-						newStatus)));
-
-		if(newStatus == SwitchStatus.MASTER  && role != OFControllerRole.ROLE_MASTER) {
-			counters.invalidSwitchActivatedWhileSlave.increment();
-			log.error("Switch {} activated but controller not MASTER", sw);
-			sw.disconnect();
-			return; // only react to switch connections when master
-		}
-
-		if(!oldStatus.isVisible() && newStatus.isVisible()) {
-			// the switch has just become visible. Send 'add' notification to our
-			// listeners
-			addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.ADDED));
-		} else if((oldStatus.isVisible() && !newStatus.isVisible())) {
-			addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED));
-		}
-
-		// note: no else if - both may be true
-		if(oldStatus != SwitchStatus.MASTER && newStatus == SwitchStatus.MASTER ) {
-			counters.switchActivated.increment();
-			addUpdateToQueue(new SwitchUpdate(dpid,
-					SwitchUpdateType.ACTIVATED));
-		} else if(oldStatus == SwitchStatus.MASTER && newStatus != SwitchStatus.MASTER ) {
-			counters.switchDeactivated.increment();
-			addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.DEACTIVATED));
-		}
-	}
-
-	@Override
-	public synchronized void switchDisconnected(IOFSwitchBackend sw) {
-		DatapathId dpid = sw.getId();
-		IOFSwitchBackend presentSw = this.switches.get(dpid);
-
-		if (presentSw != sw)  {
-			// Note == for object equality, not .equals for value
-			counters.errorActivatedSwitchNotPresent.increment();
-			log.warn("Switch {} disconnect but not present in sync manager", sw);
-			return;
-		}
-
-		counters.switchDisconnected.increment();
-		this.switches.remove(dpid);
-	}
-
-	@Override public void handshakeDisconnected(DatapathId dpid) {
-		this.switchHandlers.remove(dpid);
-	}
-
-	public Iterable<IOFSwitch> getActiveSwitches() {
-		ImmutableList.Builder<IOFSwitch> builder = ImmutableList.builder();
-		for(IOFSwitch sw: switches.values()) {
-			if(sw.getStatus().isControllable())
-				builder.add(sw);
-		}
-		return builder.build();
-	}
-
-	public Map<DatapathId, IOFSwitch> getAllSwitchMap(boolean showInvisible) {
-		if(showInvisible) {
-			return ImmutableMap.<DatapathId, IOFSwitch>copyOf(switches);
-		} else {
-			ImmutableMap.Builder<DatapathId, IOFSwitch> builder = ImmutableMap.builder();
-			for(IOFSwitch sw: switches.values()) {
-				if(sw.getStatus().isVisible())
-					builder.put(sw.getId(), sw);
-			}
-			return builder.build();
-		}
-	}
-
-	@Override
-	public Map<DatapathId, IOFSwitch> getAllSwitchMap() {
-		return getAllSwitchMap(true);
-	}
-
-	@Override
-	public Set<DatapathId> getAllSwitchDpids() {
-		return getAllSwitchMap().keySet();
-	}
-
-	public Set<DatapathId> getAllSwitchDpids(boolean showInvisible) {
-		return getAllSwitchMap(showInvisible).keySet();
-	}
-
-	@Override
-	public IOFSwitch getSwitch(DatapathId dpid) {
-		return this.switches.get(dpid);
-	}
-
-	@Override
-	public IOFSwitch getActiveSwitch(DatapathId dpid) {
-		IOFSwitchBackend sw = this.switches.get(dpid);
-		if(sw != null && sw.getStatus().isVisible())
-			return sw;
-		else
-			return null;
-	}
-
-	enum SwitchUpdateType {
-		ADDED,
-		REMOVED,
-		ACTIVATED,
-		DEACTIVATED,
-		PORTCHANGED,
-		OTHERCHANGE
-	}
-
-	/**
-	 * Update message indicating a switch was added or removed
-	 */
-	class SwitchUpdate implements IUpdate {
-		private final DatapathId swId;
-		private final SwitchUpdateType switchUpdateType;
-		private final OFPortDesc port;
-		private final PortChangeType changeType;
-
-		public SwitchUpdate(DatapathId swId, SwitchUpdateType switchUpdateType) {
-			this(swId, switchUpdateType, null, null);
-		}
-
-		public SwitchUpdate(DatapathId swId,
-				SwitchUpdateType switchUpdateType,
-				OFPortDesc port,
-				PortChangeType changeType) {
-			if (switchUpdateType == SwitchUpdateType.PORTCHANGED) {
-				if (port == null) {
-					throw new NullPointerException("Port must not be null " +
-							"for PORTCHANGED updates");
-				}
-				if (changeType == null) {
-					throw new NullPointerException("ChangeType must not be " +
-							"null for PORTCHANGED updates");
-				}
-			} else {
-				if (port != null || changeType != null) {
-					throw new IllegalArgumentException("port and changeType " +
-							"must be null for " + switchUpdateType +
-							" updates");
-				}
-			}
-			this.swId = swId;
-			this.switchUpdateType = switchUpdateType;
-			this.port = port;
-			this.changeType = changeType;
-		}
-
-		@Override
-		public void dispatch() {
-			if (log.isTraceEnabled()) {
-				log.trace("Dispatching switch update {} {}", swId, switchUpdateType);
-			}
-			if (switchListeners != null) {
-				for (IOFSwitchListener listener : switchListeners) {
-					switch(switchUpdateType) {
-					case ADDED:
-						// don't count here. We have more specific
-						// counters before the update is created
-						listener.switchAdded(swId);
-						break;
-					case REMOVED:
-						// don't count here. We have more specific
-						// counters before the update is created
-						listener.switchRemoved(swId);
-						break;
-					case PORTCHANGED:
-						counters.switchPortChanged
-						.increment();
-						listener.switchPortChanged(swId, port, changeType);
-						break;
-					case ACTIVATED:
-						// don't count here. We have more specific
-						// counters before the update is created
-						listener.switchActivated(swId);
-						break;
-					case DEACTIVATED:
-						// Called on master to slave transitions, ROLE_STATUS message.
-						listener.switchDeactivated(swId);
-						break;
-					case OTHERCHANGE:
-						counters.switchOtherChange
-						.increment();
-						listener.switchChanged(swId);
-						break;
-					}
-				}
-			}
-		}
-	}
-
-	/**
-	 * Handles a new OF Connection
-	 * @param IOFConnectionBackend connection an opened OF Connection
-	 * @param OFFeaturesReply featuresReply the features reply received for the opened connection.
-	 * It is needed for the rest of the switch handshake.
-	 */
-	@Override
-	public void connectionOpened(IOFConnectionBackend connection, OFFeaturesReply featuresReply) {
-		DatapathId dpid = connection.getDatapathId();
-		OFAuxId auxId = connection.getAuxId();
-
-		log.debug("{} opened", connection);
-
-		if(auxId.equals(OFAuxId.MAIN)) {
-
-			// Create a new switch handshake handler
-			OFSwitchHandshakeHandler handler =
-					new OFSwitchHandshakeHandler(connection, featuresReply, this,
-							floodlightProvider.getRoleManager(), floodlightProvider.getTimer());
-
-			OFSwitchHandshakeHandler oldHandler = switchHandlers.put(dpid, handler);
-
-			// Disconnect all the handler's connections
-			if(oldHandler != null){
-				log.debug("{} is a new main connection, killing old handler connections", connection);
-				oldHandler.cleanup();
-			}
-
-			handler.beginHandshake();
-
-		} else {
-			OFSwitchHandshakeHandler handler = switchHandlers.get(dpid);
-
-			if(handler != null) {
-				handler.auxConnectionOpened(connection);
-			}
-			// Connections have arrived before the switchhandler is ready
-			else {
-				log.warn("{} arrived before main connection, closing connection", connection);
-				connection.disconnect();
-			}
-		}
-	}
-
-	@Override
-	public void addSwitchEvent(DatapathId dpid, String reason, boolean flushNow) {
-		if (flushNow)
-			evSwitch.newEventWithFlush(new SwitchEvent(dpid, reason));
-		else
-			evSwitch.newEventNoFlush(new SwitchEvent(dpid, reason));
-	}
-
-	@Override
-	public synchronized void notifyPortChanged(IOFSwitchBackend sw,
-			OFPortDesc port,
-			PortChangeType changeType) {
-		Preconditions.checkNotNull(sw, "switch must not be null");
-		Preconditions.checkNotNull(port, "port must not be null");
-		Preconditions.checkNotNull(changeType, "changeType must not be null");
-
-		if (role != OFControllerRole.ROLE_MASTER) {
-			counters.invalidPortsChanged.increment();
-			return;
-		}
-		if (!this.switches.containsKey(sw.getId())) {
-			counters.invalidPortsChanged.increment();
-			return;
-		}
-
-		if(sw.getStatus().isVisible()) {
-			// no need to count here. SwitchUpdate.dispatch will count
-			// the portchanged
-			SwitchUpdate update = new SwitchUpdate(sw.getId(),
-					SwitchUpdateType.PORTCHANGED,
-					port, changeType);
-			addUpdateToQueue(update);
-		}
-	}
-
-	@Override
-	public IOFSwitchBackend getOFSwitchInstance(IOFConnectionBackend connection,
-			SwitchDescription description,
-			OFFactory factory, DatapathId datapathId) {
-
-		return this.driverRegistry.getOFSwitchInstance(connection, description, factory, datapathId);
-	}
-
-	@Override
-	public void handleMessage(IOFSwitchBackend sw, OFMessage m, FloodlightContext bContext) {
-		floodlightProvider.handleMessage(sw, m, bContext);
-	}
-
-	@Override
-	public void handleOutgoingMessage(IOFSwitch sw, OFMessage m) {
-		floodlightProvider.handleOutgoingMessage(sw, m);
-	}
-
-	@Override
-	public void addOFSwitchDriver(String manufacturerDescriptionPrefix,
-			IOFSwitchDriver driver) {
-		this.driverRegistry.addSwitchDriver(manufacturerDescriptionPrefix, driver);
-	}
-
-	@Override
-	public ImmutableList<OFSwitchHandshakeHandler> getSwitchHandshakeHandlers() {
-		return ImmutableList.copyOf(this.switchHandlers.values());
-	}
-
-	@Override
-	public int getNumRequiredConnections() {
-		Preconditions.checkState(numRequiredConnections >= 0, "numRequiredConnections not calculated");
-		return numRequiredConnections;
-	}
-
-	public Set<LogicalOFMessageCategory> getLogicalOFMessageCategories() {
-		return logicalOFMessageCategories;
-	}
-
-	private int calcNumRequiredConnections() {
-		if(!this.logicalOFMessageCategories.isEmpty()){
-			// We use tree set here to maintain ordering
-			TreeSet<OFAuxId> auxConnections = new TreeSet<OFAuxId>();
-
-			for(LogicalOFMessageCategory category : this.logicalOFMessageCategories){
-				auxConnections.add(category.getAuxId());
-			}
-
-			OFAuxId first = auxConnections.first();
-			OFAuxId last = auxConnections.last();
-
-			// Check for contiguous set (1....size())
-			if(first.equals(OFAuxId.MAIN)) {
-				if(last.getValue() != auxConnections.size() - 1){
-					throw new IllegalStateException("Logical OF message categories must maintain contiguous OF Aux Ids! i.e. (0,1,2,3,4,5)");
-				}
-				return auxConnections.size() - 1;
-			} else if(first.equals(OFAuxId.of(1))) {
-				if(last.getValue() != auxConnections.size()){
-					throw new IllegalStateException("Logical OF message categories must maintain contiguous OF Aux Ids! i.e. (1,2,3,4,5)");
-				}
-				return auxConnections.size();
-			} else {
-				throw new IllegalStateException("Logical OF message categories must start at 0 (MAIN) or 1");
-			}
-		} else {
-			return 0;
-		}
-	}
-
-	/** ISwitchService Implementation **/
-	@Override
-	public void addOFSwitchListener(IOFSwitchListener listener) {
-		this.switchListeners.add(listener);
-	}
-
-	@Override
-	public void removeOFSwitchListener(IOFSwitchListener listener) {
-		this.switchListeners.remove(listener);
-	}
-
-	@Override
-	public void registerLogicalOFMessageCategory(LogicalOFMessageCategory category) {
-		logicalOFMessageCategories.add(category);
-	}
-
-	@Override
-	public boolean isCategoryRegistered(LogicalOFMessageCategory category) {
-		return logicalOFMessageCategories.contains(category);
-	}
-
-	@Override
-	public SwitchRepresentation getSwitchRepresentation(DatapathId dpid) {
-		IOFSwitch sw = this.switches.get(dpid);
-		OFSwitchHandshakeHandler handler = this.switchHandlers.get(dpid);
-
-		if(sw != null && handler != null) {
-			return new SwitchRepresentation(sw, handler);
-		}
-		return null;
-	}
-
-	@Override
-	public List<SwitchRepresentation> getSwitchRepresentations() {
-
-		List<SwitchRepresentation> representations = new ArrayList<SwitchRepresentation>();
-
-		for(DatapathId dpid : this.switches.keySet()) {
-			SwitchRepresentation representation = getSwitchRepresentation(dpid);
-			if(representation != null) {
-				representations.add(representation);
-			}
-		}
-		return representations;
-	}
-
-	@Override
-	public void registerHandshakePlugin(IAppHandshakePluginFactory factory) {
-		Preconditions.checkState(floodlightProvider.getModuleLoaderState() == ModuleLoaderState.INIT,
-				"handshakeplugins can only be registered when the module loader is in state INIT!");
-		handshakePlugins.add(factory);
-	}
-
-	@Override
-	public List<IAppHandshakePluginFactory> getHandshakePlugins() {
-		return handshakePlugins;
-	}
-
-	/* IFloodlightModule Implementation */
-	@Override
-	public Collection<Class<? extends IFloodlightService>>
-	getModuleServices() {
-		Collection<Class<? extends IFloodlightService>> l =
-				new ArrayList<Class<? extends IFloodlightService>>();
-		l.add(IOFSwitchService.class);
-		return l;
-	}
-
-	@Override
-	public Map<Class<? extends IFloodlightService>, IFloodlightService>
-	getServiceImpls() {
-		Map<Class<? extends IFloodlightService>, IFloodlightService> m =
-				new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
-		m.put(IOFSwitchService.class, this);
-		return m;
-	}
-
-	@Override
-	public Collection<Class<? extends IFloodlightService>>
-	getModuleDependencies() {
-		Collection<Class<? extends IFloodlightService>> l = 
-				new ArrayList<Class<? extends IFloodlightService>>();
-
-		l.add(IFloodlightProviderService.class);
-		l.add(IDebugEventService.class);
-		l.add(IDebugCounterService.class);
-		l.add(ISyncService.class);
-
-		return l;
-	}
-
-	@Override
-	public void init(FloodlightModuleContext context) throws FloodlightModuleException {
-		// Module dependencies
-		floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
-		debugEventService = context.getServiceImpl(IDebugEventService.class);
-		debugCounterService = context.getServiceImpl(IDebugCounterService.class);
-		syncService = context.getServiceImpl(ISyncService.class);
-
-		// Module variables
-		switchHandlers = new ConcurrentHashMap<DatapathId, OFSwitchHandshakeHandler>();
-		switches = new ConcurrentHashMap<DatapathId, IOFSwitchBackend>();
-		syncedSwitches = new ConcurrentHashMap<DatapathId, IOFSwitch>();
-		floodlightProvider.getTimer();
-		counters = new SwitchManagerCounters(debugCounterService);
-		driverRegistry = new NaiveSwitchDriverRegistry(this);
-
-		this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
-
-		/* TODO @Ryan
+    private static final Logger log = LoggerFactory.getLogger(OFSwitchManager.class);
+
+    private static volatile OFControllerRole role;
+    private static SwitchManagerCounters counters;
+
+    private static ISyncService syncService;
+    private static IStoreClient<DatapathId, SwitchSyncRepresentation> storeClient;
+    public static final String SWITCH_SYNC_STORE_NAME = OFSwitchManager.class.getCanonicalName() + ".stateStore";
+
+    private static int tcpSendBufferSize = 4 * 1024 * 1024;
+    private static int workerThreads = 16; /* perform r/w I/O on accepted connections (switches) */
+    private static int bossThreads = 1; /* just listens and accepts on server socket; workers handle r/w I/O */
+    private static int connectionBacklog = 1000; /* pending connections boss thread will queue to accept */
+    private static int connectionTimeoutMsec = 60000; /* how long to allow TCP handshake to complete (default is 60ish secs) */
+    private static TransportPort openFlowPort = TransportPort.of(6653);
+    private static Set<IPv4Address> openFlowAddresses = new HashSet<IPv4Address>();	
+
+    private static String keyStorePassword;
+    private static String keyStore;
+
+    protected static boolean clearTablesOnInitialConnectAsMaster = false;
+    protected static boolean clearTablesOnEachTransitionToMaster = false;
+
+    protected static Map<DatapathId, TableId> forwardToControllerFlowsUpToTableByDpid;
+    protected static TableId forwardToControllerFlowsUpToTable = TableId.of(4); /* this should cover most HW switches that have a couple SW-based flow tables */
+
+    protected static List<U32> ofBitmaps;
+    protected static OFFactory defaultFactory;
+
+    private static ConcurrentHashMap<DatapathId, OFSwitchHandshakeHandler> switchHandlers;
+    private static ConcurrentHashMap<DatapathId, IOFSwitchBackend> switches;
+    private static ConcurrentHashMap<DatapathId, IOFSwitch> syncedSwitches;
+
+    protected static Map<DatapathId, OFControllerRole> switchInitialRole;
+
+    private static ISwitchDriverRegistry driverRegistry;
+
+    private Set<LogicalOFMessageCategory> logicalOFMessageCategories = new CopyOnWriteArraySet<LogicalOFMessageCategory>();
+    private static final List<IAppHandshakePluginFactory> handshakePlugins = new CopyOnWriteArrayList<IAppHandshakePluginFactory>();
+    private static int numRequiredConnections = -1;
+
+    // ISwitchService
+    protected static Set<IOFSwitchListener> switchListeners;
+
+    // Module Dependencies
+    private static IFloodlightProviderService floodlightProvider;
+    private static IDebugCounterService debugCounterService;
+
+    private static NioEventLoopGroup bossGroup;
+    private static NioEventLoopGroup workerGroup;
+    private static DefaultChannelGroup cg;
+
+    protected static Timer timer;
+
+    /** IHAListener Implementation **/
+    @Override
+    public void transitionToActive() {
+        role = HARole.ACTIVE.getOFRole();
+    }
+
+    @Override
+    public void transitionToStandby() {
+        role = HARole.STANDBY.getOFRole();
+    }
+
+    /** IOFSwitchManager Implementation **/
+
+    @Override public SwitchManagerCounters getCounters() {
+        return counters;
+    }
+
+    private void addUpdateToQueue(IUpdate iUpdate) {
+        floodlightProvider.addUpdateToQueue(iUpdate);
+    }
+
+    @Override
+    public synchronized void switchAdded(IOFSwitchBackend sw) {
+        DatapathId dpid = sw.getId();
+        IOFSwitchBackend oldSw = switches.put(dpid, sw);
+
+        if (oldSw == sw)  {
+            // Note == for object equality, not .equals for value
+            counters.errorActivatedSwitchNotPresent.increment();
+            log.error("Switch {} added twice?", sw);
+            return;
+        } else if (oldSw != null) {
+            // This happens either when we have switches with duplicate
+            // DPIDs or when a switch reconnects before we saw the
+            // disconnect
+            counters.switchWithSameDpidActivated.increment();
+            log.warn("New switch added {} for already-added switch {}", sw, oldSw);
+            // We need to disconnect and remove the old switch
+            oldSw.cancelAllPendingRequests();
+            addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED));
+            oldSw.disconnect();
+        }
+
+        /*
+         * Set some other config options for this switch.
+         */
+        if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
+            if (forwardToControllerFlowsUpToTableByDpid.containsKey(sw.getId())) {
+                sw.setMaxTableForTableMissFlow(forwardToControllerFlowsUpToTableByDpid.get(sw.getId()));
+            } else {
+                sw.setMaxTableForTableMissFlow(forwardToControllerFlowsUpToTable);
+            }
+        }
+    }
+
+    @Override
+    public synchronized void switchStatusChanged(IOFSwitchBackend sw, SwitchStatus oldStatus, SwitchStatus newStatus) {
+        DatapathId dpid = sw.getId();
+        IOFSwitchBackend presentSw = switches.get(dpid);
+
+        if (presentSw != sw)  {
+            // Note == for object equality, not .equals for value
+            counters.errorActivatedSwitchNotPresent
+            .increment();
+            log.debug("Switch {} status change but not present in sync manager", sw);
+            return;
+        }
+
+        if(newStatus == SwitchStatus.MASTER  && role != OFControllerRole.ROLE_MASTER) {
+            counters.invalidSwitchActivatedWhileSlave.increment();
+            log.error("Switch {} activated but controller not MASTER", sw);
+            sw.disconnect();
+            return; // only react to switch connections when master
+        }
+
+        if(!oldStatus.isVisible() && newStatus.isVisible()) {
+            // the switch has just become visible. Send 'add' notification to our
+            // listeners
+            addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.ADDED));
+        } else if((oldStatus.isVisible() && !newStatus.isVisible())) {
+            addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED));
+        }
+
+        // note: no else if - both may be true
+        if(oldStatus != SwitchStatus.MASTER && newStatus == SwitchStatus.MASTER ) {
+            counters.switchActivated.increment();
+            addUpdateToQueue(new SwitchUpdate(dpid,
+                    SwitchUpdateType.ACTIVATED));
+        } else if(oldStatus == SwitchStatus.MASTER && newStatus != SwitchStatus.MASTER ) {
+            counters.switchDeactivated.increment();
+            addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.DEACTIVATED));
+        }
+    }
+
+    @Override
+    public synchronized void switchDisconnected(IOFSwitchBackend sw) {
+        DatapathId dpid = sw.getId();
+        IOFSwitchBackend presentSw = switches.get(dpid);
+
+        if (presentSw != sw)  {
+            // Note == for object equality, not .equals for value
+            counters.errorActivatedSwitchNotPresent.increment();
+            log.warn("Switch {} disconnect but not present in sync manager", sw);
+            return;
+        }
+
+        counters.switchDisconnected.increment();
+        switches.remove(dpid);
+    }
+
+    @Override public void handshakeDisconnected(DatapathId dpid) {
+        switchHandlers.remove(dpid);
+    }
+
+    public Iterable<IOFSwitch> getActiveSwitches() {
+        ImmutableList.Builder<IOFSwitch> builder = ImmutableList.builder();
+        for(IOFSwitch sw: switches.values()) {
+            if(sw.getStatus().isControllable())
+                builder.add(sw);
+        }
+        return builder.build();
+    }
+
+    public Map<DatapathId, IOFSwitch> getAllSwitchMap(boolean showInvisible) {
+        if(showInvisible) {
+            return ImmutableMap.<DatapathId, IOFSwitch>copyOf(switches);
+        } else {
+            ImmutableMap.Builder<DatapathId, IOFSwitch> builder = ImmutableMap.builder();
+            for(IOFSwitch sw: switches.values()) {
+                if(sw.getStatus().isVisible())
+                    builder.put(sw.getId(), sw);
+            }
+            return builder.build();
+        }
+    }
+
+    @Override
+    public Map<DatapathId, IOFSwitch> getAllSwitchMap() {
+        return getAllSwitchMap(true);
+    }
+
+    @Override
+    public Set<DatapathId> getAllSwitchDpids() {
+        return getAllSwitchMap().keySet();
+    }
+
+    public Set<DatapathId> getAllSwitchDpids(boolean showInvisible) {
+        return getAllSwitchMap(showInvisible).keySet();
+    }
+
+    @Override
+    public IOFSwitch getSwitch(DatapathId dpid) {
+        return switches.get(dpid);
+    }
+
+    @Override
+    public IOFSwitch getActiveSwitch(DatapathId dpid) {
+        IOFSwitchBackend sw = switches.get(dpid);
+        if(sw != null && sw.getStatus().isVisible())
+            return sw;
+        else
+            return null;
+    }
+
+    enum SwitchUpdateType {
+        ADDED,
+        REMOVED,
+        ACTIVATED,
+        DEACTIVATED,
+        PORTCHANGED,
+        OTHERCHANGE
+    }
+
+    /**
+     * Update message indicating a switch was added or removed
+     */
+    class SwitchUpdate implements IUpdate {
+        private final DatapathId swId;
+        private final SwitchUpdateType switchUpdateType;
+        private final OFPortDesc port;
+        private final PortChangeType changeType;
+
+        public SwitchUpdate(DatapathId swId, SwitchUpdateType switchUpdateType) {
+            this(swId, switchUpdateType, null, null);
+        }
+
+        public SwitchUpdate(DatapathId swId,
+                SwitchUpdateType switchUpdateType,
+                OFPortDesc port,
+                PortChangeType changeType) {
+            if (switchUpdateType == SwitchUpdateType.PORTCHANGED) {
+                if (port == null) {
+                    throw new NullPointerException("Port must not be null " +
+                            "for PORTCHANGED updates");
+                }
+                if (changeType == null) {
+                    throw new NullPointerException("ChangeType must not be " +
+                            "null for PORTCHANGED updates");
+                }
+            } else {
+                if (port != null || changeType != null) {
+                    throw new IllegalArgumentException("port and changeType " +
+                            "must be null for " + switchUpdateType +
+                            " updates");
+                }
+            }
+            this.swId = swId;
+            this.switchUpdateType = switchUpdateType;
+            this.port = port;
+            this.changeType = changeType;
+        }
+
+        @Override
+        public void dispatch() {
+            if (log.isTraceEnabled()) {
+                log.trace("Dispatching switch update {} {}", swId, switchUpdateType);
+            }
+            if (switchListeners != null) {
+                for (IOFSwitchListener listener : switchListeners) {
+                    switch(switchUpdateType) {
+                    case ADDED:
+                        // don't count here. We have more specific
+                        // counters before the update is created
+                        listener.switchAdded(swId);
+                        break;
+                    case REMOVED:
+                        // don't count here. We have more specific
+                        // counters before the update is created
+                        listener.switchRemoved(swId);
+                        break;
+                    case PORTCHANGED:
+                        counters.switchPortChanged
+                        .increment();
+                        listener.switchPortChanged(swId, port, changeType);
+                        break;
+                    case ACTIVATED:
+                        // don't count here. We have more specific
+                        // counters before the update is created
+                        listener.switchActivated(swId);
+                        break;
+                    case DEACTIVATED:
+                        // Called on master to slave transitions, ROLE_STATUS message.
+                        listener.switchDeactivated(swId);
+                        break;
+                    case OTHERCHANGE:
+                        counters.switchOtherChange
+                        .increment();
+                        listener.switchChanged(swId);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Handles a new OF Connection
+     * @param IOFConnectionBackend connection an opened OF Connection
+     * @param OFFeaturesReply featuresReply the features reply received for the opened connection.
+     * It is needed for the rest of the switch handshake.
+     */
+    @Override
+    public void connectionOpened(IOFConnectionBackend connection, OFFeaturesReply featuresReply) {
+        DatapathId dpid = connection.getDatapathId();
+        OFAuxId auxId = connection.getAuxId();
+
+        log.debug("{} opened", connection);
+
+        if(auxId.equals(OFAuxId.MAIN)) {
+
+            // Create a new switch handshake handler
+            OFSwitchHandshakeHandler handler =
+                    new OFSwitchHandshakeHandler(connection, featuresReply, this,
+                            floodlightProvider.getRoleManager(), timer);
+
+            OFSwitchHandshakeHandler oldHandler = switchHandlers.put(dpid, handler);
+
+            // Disconnect all the handler's connections
+            if(oldHandler != null){
+                log.debug("{} is a new main connection, killing old handler connections", connection);
+                oldHandler.cleanup();
+            }
+
+            handler.beginHandshake();
+
+        } else {
+            OFSwitchHandshakeHandler handler = switchHandlers.get(dpid);
+
+            if(handler != null) {
+                handler.auxConnectionOpened(connection);
+            }
+            // Connections have arrived before the switchhandler is ready
+            else {
+                log.warn("{} arrived before main connection, closing connection", connection);
+                connection.disconnect();
+            }
+        }
+    }
+
+    @Override
+    public synchronized void notifyPortChanged(IOFSwitchBackend sw,
+            OFPortDesc port,
+            PortChangeType changeType) {
+        Preconditions.checkNotNull(sw, "switch must not be null");
+        Preconditions.checkNotNull(port, "port must not be null");
+        Preconditions.checkNotNull(changeType, "changeType must not be null");
+
+        if (role != OFControllerRole.ROLE_MASTER) {
+            counters.invalidPortsChanged.increment();
+            return;
+        }
+        if (!switches.containsKey(sw.getId())) {
+            counters.invalidPortsChanged.increment();
+            return;
+        }
+
+        if(sw.getStatus().isVisible()) {
+            // no need to count here. SwitchUpdate.dispatch will count
+            // the portchanged
+            SwitchUpdate update = new SwitchUpdate(sw.getId(),
+                    SwitchUpdateType.PORTCHANGED,
+                    port, changeType);
+            addUpdateToQueue(update);
+        }
+    }
+
+    @Override
+    public IOFSwitchBackend getOFSwitchInstance(IOFConnectionBackend connection,
+            SwitchDescription description,
+            OFFactory factory, DatapathId datapathId) {
+
+        return driverRegistry.getOFSwitchInstance(connection, description, factory, datapathId);
+    }
+
+    @Override
+    public void handleMessage(IOFSwitchBackend sw, OFMessage m, FloodlightContext bContext) {
+        floodlightProvider.handleMessage(sw, m, bContext);
+    }
+
+    @Override
+    public void handleOutgoingMessage(IOFSwitch sw, OFMessage m) {
+        floodlightProvider.handleOutgoingMessage(sw, m);
+    }
+
+    @Override
+    public void addOFSwitchDriver(String manufacturerDescriptionPrefix,
+            IOFSwitchDriver driver) {
+        driverRegistry.addSwitchDriver(manufacturerDescriptionPrefix, driver);
+    }
+
+    @Override
+    public ImmutableList<OFSwitchHandshakeHandler> getSwitchHandshakeHandlers() {
+        return ImmutableList.copyOf(switchHandlers.values());
+    }
+
+    @Override
+    public int getNumRequiredConnections() {
+        Preconditions.checkState(numRequiredConnections >= 0, "numRequiredConnections not calculated");
+        return numRequiredConnections;
+    }
+
+    public Set<LogicalOFMessageCategory> getLogicalOFMessageCategories() {
+        return logicalOFMessageCategories;
+    }
+
+    private int calcNumRequiredConnections() {
+        if(!logicalOFMessageCategories.isEmpty()){
+            // We use tree set here to maintain ordering
+            TreeSet<OFAuxId> auxConnections = new TreeSet<OFAuxId>();
+
+            for(LogicalOFMessageCategory category : logicalOFMessageCategories){
+                auxConnections.add(category.getAuxId());
+            }
+
+            OFAuxId first = auxConnections.first();
+            OFAuxId last = auxConnections.last();
+
+            // Check for contiguous set (1....size())
+            if(first.equals(OFAuxId.MAIN)) {
+                if(last.getValue() != auxConnections.size() - 1){
+                    throw new IllegalStateException("Logical OF message categories must maintain contiguous OF Aux Ids! i.e. (0,1,2,3,4,5)");
+                }
+                return auxConnections.size() - 1;
+            } else if(first.equals(OFAuxId.of(1))) {
+                if(last.getValue() != auxConnections.size()){
+                    throw new IllegalStateException("Logical OF message categories must maintain contiguous OF Aux Ids! i.e. (1,2,3,4,5)");
+                }
+                return auxConnections.size();
+            } else {
+                throw new IllegalStateException("Logical OF message categories must start at 0 (MAIN) or 1");
+            }
+        } else {
+            return 0;
+        }
+    }
+
+    /** ISwitchService Implementation **/
+    @Override
+    public void addOFSwitchListener(IOFSwitchListener listener) {
+        switchListeners.add(listener);
+    }
+
+    @Override
+    public void removeOFSwitchListener(IOFSwitchListener listener) {
+        switchListeners.remove(listener);
+    }
+
+    @Override
+    public void registerLogicalOFMessageCategory(LogicalOFMessageCategory category) {
+        logicalOFMessageCategories.add(category);
+    }
+
+    @Override
+    public boolean isCategoryRegistered(LogicalOFMessageCategory category) {
+        return logicalOFMessageCategories.contains(category);
+    }
+
+    @Override
+    public void registerHandshakePlugin(IAppHandshakePluginFactory factory) {
+        Preconditions.checkState(floodlightProvider.getModuleLoaderState() == ModuleLoaderState.INIT,
+                "handshakeplugins can only be registered when the module loader is in state INIT!");
+        handshakePlugins.add(factory);
+    }
+
+    @Override
+    public List<IAppHandshakePluginFactory> getHandshakePlugins() {
+        return handshakePlugins;
+    }
+
+    /* IFloodlightModule Implementation */
+    @Override
+    public Collection<Class<? extends IFloodlightService>>
+    getModuleServices() {
+        Collection<Class<? extends IFloodlightService>> l =
+                new ArrayList<Class<? extends IFloodlightService>>();
+        l.add(IOFSwitchService.class);
+        return l;
+    }
+
+    @Override
+    public Map<Class<? extends IFloodlightService>, IFloodlightService>
+    getServiceImpls() {
+        Map<Class<? extends IFloodlightService>, IFloodlightService> m =
+                new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+        m.put(IOFSwitchService.class, this);
+        return m;
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>>
+    getModuleDependencies() {
+        Collection<Class<? extends IFloodlightService>> l = 
+                new ArrayList<Class<? extends IFloodlightService>>();
+
+        l.add(IFloodlightProviderService.class);
+        l.add(IDebugCounterService.class);
+        l.add(ISyncService.class);
+
+        return l;
+    }
+
+    @Override
+    public void init(FloodlightModuleContext context) throws FloodlightModuleException {
+        // Module dependencies
+        floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+        debugCounterService = context.getServiceImpl(IDebugCounterService.class);
+        syncService = context.getServiceImpl(ISyncService.class);
+
+        // Module variables
+        switchHandlers = new ConcurrentHashMap<DatapathId, OFSwitchHandshakeHandler>();
+        switches = new ConcurrentHashMap<DatapathId, IOFSwitchBackend>();
+        syncedSwitches = new ConcurrentHashMap<DatapathId, IOFSwitch>();
+        counters = new SwitchManagerCounters(debugCounterService);
+        driverRegistry = new NaiveSwitchDriverRegistry(this);
+
+        switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
+
+        timer = new HashedWheelTimer();
+
+        /* TODO
 		try {
-			this.storeClient = this.syncService.getStoreClient(
+			storeClient = syncService.getStoreClient(
 					SWITCH_SYNC_STORE_NAME,
 					DatapathId.class,
 					SwitchSyncRepresentation.class);
-			this.storeClient.addStoreListener(this);
+			storeClient.addStoreListener(this);
 		} catch (UnknownStoreException e) {
 			throw new FloodlightModuleException("Error while setting up sync store client", e);
 		} */
 
-		/* 
-		 * Get SSL config.
-		 * 
-		 * If a password is blank, the password field may or may not be specified.
-		 * If it is specified, an empty string will be expected for blank.
-		 * 
-		 * The path MUST be specified if SSL is enabled.
-		 */
-		Map<String, String> configParams = context.getConfigParams(this);
-		String path = configParams.get("keyStorePath");
-		String pass = configParams.get("keyStorePassword");
-		String useSsl = configParams.get("useSsl");
-
-		if (useSsl == null || path == null || path.isEmpty() || 
-				(!useSsl.equalsIgnoreCase("yes") && !useSsl.equalsIgnoreCase("true") &&
-						!useSsl.equalsIgnoreCase("yep") && !useSsl.equalsIgnoreCase("ja") &&
-						!useSsl.equalsIgnoreCase("stimmt")
-						)
-				) {
-			log.warn("SSL disabled. Using unsecure connections between Floodlight and switches.");
-			OFSwitchManager.keyStore = null;
-			OFSwitchManager.keyStorePassword = null;
-		} else {
-			log.info("SSL enabled. Using secure connections between Floodlight and switches.");
-			log.info("SSL keystore path: {}, password: {}", path, (pass == null ? "" : pass)); 
-			OFSwitchManager.keyStore = path;
-			OFSwitchManager.keyStorePassword = (pass == null ? "" : pass);
-		}
-
-		/*
-		 * Get config to define what to do when a switch connects.
-		 * 
-		 * If a field is blank or unspecified, it will default
-		 */
-		String clearInitial = configParams.get("clearTablesOnInitialHandshakeAsMaster");
-		String clearLater = configParams.get("clearTablesOnEachTransitionToMaster");
-
-		if (clearInitial == null || clearInitial.isEmpty() || 
-				(!clearInitial.equalsIgnoreCase("yes") && !clearInitial.equalsIgnoreCase("true") &&
-						!clearInitial.equalsIgnoreCase("yep") && !clearInitial.equalsIgnoreCase("ja") &&
-						!clearInitial.equalsIgnoreCase("stimmt"))) {
-			log.info("Clear switch flow tables on initial handshake as master: FALSE");
-			OFSwitchManager.clearTablesOnInitialConnectAsMaster = false;
-		} else {
-			log.info("Clear switch flow tables on initial handshake as master: TRUE");
-			OFSwitchManager.clearTablesOnInitialConnectAsMaster = true;
-		}
-
-		if (clearLater == null || clearLater.isEmpty() || 
-				(!clearLater.equalsIgnoreCase("yes") && !clearLater.equalsIgnoreCase("true") &&
-						!clearLater.equalsIgnoreCase("yep") && !clearLater.equalsIgnoreCase("ja") &&
-						!clearLater.equalsIgnoreCase("stimmt"))) {
-			log.info("Clear switch flow tables on each transition to master: FALSE");
-			OFSwitchManager.clearTablesOnEachTransitionToMaster = false;
-		} else {
-			log.info("Clear switch flow tables on each transition to master: TRUE");
-			OFSwitchManager.clearTablesOnEachTransitionToMaster = true;
-		}
-		
-		
-		//Define initial role per switch		
-		String switchesInitialState = configParams.get("switchesInitialState");
-		switchInitialRole = jsonToSwitchInitialRoleMap(switchesInitialState);
-		log.debug("SwitchInitialRole: {}", switchInitialRole.entrySet());
-
-		/*
-		 * Get default max table for forward to controller flows. 
-		 * Internal default set as class variable at top of OFSwitchManager.
-		 */
-		String defaultFlowsUpToTable = configParams.get("defaultMaxTablesToReceiveTableMissFlow");
-		/* Backward compatibility */
-		if (defaultFlowsUpToTable == null || defaultFlowsUpToTable.isEmpty()) {
-			defaultFlowsUpToTable = configParams.get("defaultMaxTableToReceiveTableMissFlow");
-		}
-		if (defaultFlowsUpToTable != null && !defaultFlowsUpToTable.isEmpty()) {
-			defaultFlowsUpToTable = defaultFlowsUpToTable.toLowerCase().trim();
-			try {
-				forwardToControllerFlowsUpToTable = TableId.of(defaultFlowsUpToTable.startsWith("0x") 
-						? Integer.parseInt(defaultFlowsUpToTable.replaceFirst("0x", ""), 16) 
-								: Integer.parseInt(defaultFlowsUpToTable));
-				log.info("Setting {} as the default max tables to receive table-miss flow", forwardToControllerFlowsUpToTable.toString());
-			} catch (IllegalArgumentException e) {
-				log.error("Invalid table ID {} for default max tables to receive table-miss flow. Using pre-set of {}", 
-						defaultFlowsUpToTable, forwardToControllerFlowsUpToTable.toString());
-			}
-		} else {
-			log.info("Default max tables to receive table-miss flow not configured. Using {}", forwardToControllerFlowsUpToTable.toString());
-		}
-
-		/*
-		 * Get config to define which tables per switch will get a
-		 * default forward-to-controller flow. This can be used to
-		 * reduce the number of such flows if only a reduced set of
-		 * tables are being used.
-		 */
-		String maxPerDpid = configParams.get("maxTablesToReceiveTableMissFlowPerDpid");
-		/* Backward compatibility */
-		if (maxPerDpid == null || maxPerDpid.isEmpty()) {
-			maxPerDpid = configParams.get("maxTableToReceiveTableMissFlowPerDpid");
-		}
-		forwardToControllerFlowsUpToTableByDpid = jsonToSwitchTableIdMap(maxPerDpid);
-
-		/*
-		 * Get config to determine what versions of OpenFlow we will
-		 * support. The versions will determine the hello's header
-		 * version as well as the OF1.3.1 version bitmap contents.
-		 */
-		String protocols = configParams.get("supportedOpenFlowVersions");
-		Set<OFVersion> ofVersions = new HashSet<OFVersion>();
-		if (protocols != null && !protocols.isEmpty()) {
-			protocols = protocols.toLowerCase();
-			/* 
-			 * Brute-force check for all known versions. 
-			 */
-			if (protocols.contains("1.0") || protocols.contains("10")) {
-				ofVersions.add(OFVersion.OF_10);
-			}
-			if (protocols.contains("1.1") || protocols.contains("11")) {
-				ofVersions.add(OFVersion.OF_11);
-			}
-			if (protocols.contains("1.2") || protocols.contains("12")) {
-				ofVersions.add(OFVersion.OF_12);
-			}
-			if (protocols.contains("1.3") || protocols.contains("13")) {
-				ofVersions.add(OFVersion.OF_13);
-			}
-			if (protocols.contains("1.4") || protocols.contains("14")) {
-				ofVersions.add(OFVersion.OF_14);
-			}
-			/*
-			 * TODO This will need to be updated if/when 
-			 * Loxi is updated to support > 1.4.
-			 * 
-			 * if (protocols.contains("1.5") || protocols.contains("15")) {
-			 *     ofVersions.add(OFVersion.OF_15);
-			 * }
-			 */
-		} else {
-			log.warn("Supported OpenFlow versions not specified. Using Loxi-defined {}", OFVersion.values());
-			ofVersions.addAll(Arrays.asList(OFVersion.values()));
-		}
-		/* Sanity check */
-		if (ofVersions.isEmpty()) {
-			throw new IllegalStateException("OpenFlow version list should never be empty at this point. Make sure it's being populated in OFSwitchManager's init function.");
-		}
-		defaultFactory = computeInitialFactory(ofVersions);
-		ofBitmaps = computeOurVersionBitmaps(ofVersions);
-	}
-
-	/**
-	 * Find the max version supplied in the supported
-	 * versions list and use it as the default, which
-	 * will subsequently be used in our hello message
-	 * header's version field.
-	 * 
-	 * The factory can be later "downgraded" to a lower
-	 * version depending on what's computed during the
-	 * version-negotiation part of the handshake.
-	 * 
-	 * Assumption: The Set of OFVersion ofVersions
-	 * variable has been set already and is NOT EMPTY.
-	 * 
-	 * @return the highest-version OFFactory we support
-	 */
-	private OFFactory computeInitialFactory(Set<OFVersion> ofVersions) {
-		/* This should NEVER happen. Double-checking. */
-		if (ofVersions == null || ofVersions.isEmpty()) {
-			throw new IllegalStateException("OpenFlow version list should never be null or empty at this point. Make sure it's set in the OFSwitchManager.");
-		}
-		OFVersion highest = null;
-		for (OFVersion v : ofVersions) {
-			if (highest == null) {
-				highest = v;
-			} else if (v.compareTo(highest) > 0) {
-				highest = v;
-			}
-		}
-		/* 
-		 * This assumes highest != null, which
-		 * it won't be if the list of versions
-		 * is not empty.
-		 */
-		return OFFactories.getFactory(highest);
-	}
-
-	/**
-	 * Based on the list of OFVersions provided as input (or from Loxi),
-	 * create a list of bitmaps for use in version negotiation during a
-	 * cross-version OpenFlow handshake where both parties support 
-	 * OpenFlow versions >= 1.3.1.
-	 * 
-	 * Type Set is used as input to guarantee all unique versions.
-	 * 
-	 * @param ofVersions, the list of bitmaps. Supply to an OFHello message.
-	 * @return list of bitmaps for the versions of OpenFlow we support
-	 */
-	private List<U32> computeOurVersionBitmaps(Set<OFVersion> ofVersions) {
-		/* This should NEVER happen. Double-checking. */
-		if (ofVersions == null || ofVersions.isEmpty()) {
-			throw new IllegalStateException("OpenFlow version list should never be null or empty at this point. Make sure it's set in the OFSwitchManager.");
-		}
-
-		int pos = 1; /* initial bitmap in list */
-		int size = 32; /* size of a U32 */
-		int tempBitmap = 0; /* maintain the current bitmap we're working on */
-		List<U32> bitmaps = new ArrayList<U32>();
-		ArrayList<OFVersion> sortedVersions = new ArrayList<OFVersion>(ofVersions);
-		Collections.sort(sortedVersions);
-		for (OFVersion v : sortedVersions) {
-			/* Move on to another bitmap */
-			if (v.getWireVersion() > pos * size - 1 ) {
-				bitmaps.add(U32.ofRaw(tempBitmap));
-				tempBitmap = 0;
-				pos++;
-			}
-			tempBitmap = tempBitmap | (1 << (v.getWireVersion() % size));
-		}
-		if (tempBitmap != 0) {
-			bitmaps.add(U32.ofRaw(tempBitmap));
-		}
-		log.info("Computed OpenFlow version bitmap as {}", Arrays.asList(tempBitmap));
-		return bitmaps;
-	}
-
-	private static Map<DatapathId, TableId> jsonToSwitchTableIdMap(String json) {
-		MappingJsonFactory f = new MappingJsonFactory();
-		JsonParser jp;
-		Map<DatapathId, TableId> retValue = new HashMap<DatapathId, TableId>();
-
-		if (json == null || json.isEmpty()) {
-			return retValue;
-		}
-
-		try {
-			try {
-				jp = f.createParser(json);
-			} catch (JsonParseException e) {
-				throw new IOException(e);
-			}
-
-			jp.nextToken();
-			if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
-				throw new IOException("Expected START_OBJECT");
-			}
-
-			while (jp.nextToken() != JsonToken.END_OBJECT) {
-				if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
-					throw new IOException("Expected FIELD_NAME");
-				}
-
-				String n = jp.getCurrentName();
-				jp.nextToken();
-				if (jp.getText().equals("")) {
-					continue;
-				}
-
-				DatapathId dpid;
-				try {
-					n = n.trim();
-					dpid = DatapathId.of(n);
-
-					TableId tablesToGetDefaultFlow;
-					String value = jp.getText();
-					if (value != null && !value.isEmpty()) {
-						value = value.trim().toLowerCase();
-						try {
-							tablesToGetDefaultFlow = TableId.of(
-									value.startsWith("0x") 
-									? Integer.parseInt(value.replaceFirst("0x", ""), 16) 
-											: Integer.parseInt(value)
-									); /* will throw exception if outside valid TableId number range */
-							retValue.put(dpid, tablesToGetDefaultFlow);
-							log.info("Setting max tables to receive table-miss flow to {} for DPID {}", 
-									tablesToGetDefaultFlow.toString(), dpid.toString());
-						} catch (IllegalArgumentException e) { /* catches both IllegalArgumentExcpt. and NumberFormatExcpt. */
-							log.error("Invalid value of {} for max tables to receive table-miss flow for DPID {}. Using default of {}.", value, dpid.toString());
-						}
-					}
-				} catch (NumberFormatException e) {
-					log.error("Invalid DPID format {} for max tables to receive table-miss flow for specific DPID. Using default for the intended DPID.", n);
-				}
-			}
-		} catch (IOException e) {
-			log.error("Using default for remaining DPIDs. JSON formatting error in max tables to receive table-miss flow for DPID input String: {}", e);
-		}
-		return retValue;
-	}
-
-	@Override
-	public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
-		startUpBase(context);
-		bootstrapNetty();
-	}
-
-	/**
-	 * Startup method that includes everything besides the netty boostrap.
-	 * This has been isolated for testing.
-	 * @param context floodlight module context
-	 * @throws FloodlightModuleException
-	 */
-	public void startUpBase(FloodlightModuleContext context) throws FloodlightModuleException {
-		// Initial Role
-		role = floodlightProvider.getRole().getOFRole();
-
-		// IRoleListener
-		floodlightProvider.addHAListener(this);
-
-		loadLogicalCategories();
-
-		registerDebugEvents();
-	}
-
-	/**
-	 * Bootstraps netty, the server that handles all openflow connections
-	 */
-	public void bootstrapNetty() {
-		try {
-			bossGroup = new NioEventLoopGroup();
-			workerGroup = new NioEventLoopGroup();
-
-			ServerBootstrap bootstrap = new ServerBootstrap()
-			.group(bossGroup, workerGroup)
-			.channel(NioServerSocketChannel.class)
-			.option(ChannelOption.SO_REUSEADDR, true)
-			.option(ChannelOption.SO_KEEPALIVE, true)
-			.option(ChannelOption.TCP_NODELAY, true)
-			.option(ChannelOption.SO_SNDBUF, Controller.SEND_BUFFER_SIZE);
-
-
-			OFChannelInitializer initializer = new OFChannelInitializer(
-					this, 
-					this, 
-					debugCounterService, 
-					floodlightProvider.getTimer(), 
-					ofBitmaps, 
-					defaultFactory, 
-					keyStore, 
-					keyStorePassword);
-
-			bootstrap.childHandler(initializer);
-
-			cg = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
-
-			Set<InetSocketAddress> addrs = new HashSet<InetSocketAddress>();
-			if (floodlightProvider.getOFAddresses().isEmpty()) {
-				cg.add(bootstrap.bind(new InetSocketAddress(InetAddress.getByAddress(IPv4Address.NONE.getBytes()), floodlightProvider.getOFPort().getPort())).channel());
-			} else {
-				for (IPv4Address ip : floodlightProvider.getOFAddresses()) {
-					addrs.add(new InetSocketAddress(InetAddress.getByAddress(ip.getBytes()), floodlightProvider.getOFPort().getPort()));
-				}
-			}
-			
-			for (InetSocketAddress sa : addrs) {
-				cg.add(bootstrap.bind(sa).channel());
-				log.info("Listening for switch connections on {}", sa);
-			}
-
-		} catch (Exception e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	/**
-	 * Performs startup related actions for logical OF message categories.
-	 * Setting the categories list to immutable ensures that unsupported operation
-	 * exceptions will be activated if modifications are attempted.
-	 */
-	public void loadLogicalCategories() {
-		logicalOFMessageCategories = ImmutableSet.copyOf(logicalOFMessageCategories);
-		numRequiredConnections = calcNumRequiredConnections();
-	}
-
-	/**
-	 * Registers an event handler with the debug event service
-	 * for switch events.
-	 * @throws FloodlightModuleException
-	 */
-	private void registerDebugEvents() throws FloodlightModuleException {
-		if (debugEventService == null) {
-			debugEventService = new MockDebugEventService();
-		}
-		evSwitch = debugEventService.buildEvent(SwitchEvent.class)
-				.setModuleName(this.counters.getPrefix())
-				.setEventName("switch-event")
-				.setEventDescription("Switch connected, disconnected or port changed")
-				.setEventType(EventType.ALWAYS_LOG)
-				.setBufferCapacity(100)
-				.register();
-	}
-
-	@Override
-	public String getName() {
-		return null;
-	}
-
-	@Override
-	public boolean isCallbackOrderingPrereq(HAListenerTypeMarker type, String name) {
-		return false;
-	}
-
-	@Override
-	public boolean isCallbackOrderingPostreq(HAListenerTypeMarker type, String name) {
-		return false;
-	}
-
-	@Override
-	public void controllerNodeIPsChanged(Map<String, String> curControllerNodeIPs,
-			Map<String, String> addedControllerNodeIPs,
-			Map<String, String> removedControllerNodeIPs) {		
-	}
-
-	@Override
-	public void keysModified(Iterator<DatapathId> keys, UpdateType type) {
-		if (type == UpdateType.LOCAL) {
-			// We only care for remote updates
-			return;
-		}
-		while(keys.hasNext()) {
-			DatapathId key = keys.next();
-			Versioned<SwitchSyncRepresentation> versionedSwitch = null;
-			try {
-				versionedSwitch = storeClient.get(key);
-			} catch (SyncException e) {
-				log.error("Exception while retrieving switch " + key.toString() +
-						" from sync store. Skipping", e);
-				continue;
-			}
-			if (log.isTraceEnabled()) {
-				log.trace("Reveiced switch store notification: key={}, " +
-						"entry={}", key, versionedSwitch.getValue());
-			}
-			// versionedSwtich won't be null. storeClient.get() always
-			// returns a non-null or throws an exception
-			if (versionedSwitch.getValue() == null) {
-				switchRemovedFromStore(key);
-				continue;
-			}
-			SwitchSyncRepresentation storedSwitch = versionedSwitch.getValue();
-			IOFSwitch sw = getSwitch(storedSwitch.getDpid());
-			//TODO @Ryan need to get IOFSwitchBackend setFeaturesReply(storedSwitch.getFeaturesReply(sw.getOFFactory()));
-			if (!key.equals(storedSwitch.getFeaturesReply(sw.getOFFactory()).getDatapathId())) {
-				log.error("Inconsistent DPIDs from switch sync store: " +
-						"key is {} but sw.getId() says {}. Ignoring",
-						key.toString(), sw.getId());
-				continue;
-			}
-			switchAddedToStore(sw);
-		}
-	}
-
-	/**
-	 * Called when we receive a store notification about a switch that
-	 * has been removed from the sync store
-	 * @param dpid
-	 */
-	private synchronized void switchRemovedFromStore(DatapathId dpid) {
-		if (floodlightProvider.getRole() != HARole.STANDBY) {
-			return; // only read from store if slave
-		}
-		IOFSwitch oldSw = syncedSwitches.remove(dpid);
-		if (oldSw != null) {
-			addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED));
-		} else {
-			// TODO: the switch was deleted (tombstone) before we ever
-			// knew about it (or was deleted repeatedly). Can this
-			// happen? When/how?
-		}
-	}
-
-	/**
-	 * Called when we receive a store notification about a new or updated
-	 * switch.
-	 * @param sw
-	 */
-	private synchronized void switchAddedToStore(IOFSwitch sw) {
-		if (floodlightProvider.getRole() != HARole.STANDBY) {
-			return; // only read from store if slave
-		}
-		DatapathId dpid = sw.getId();
-
-		IOFSwitch oldSw = syncedSwitches.put(dpid, sw);
-		if (oldSw == null)  {
-			addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.ADDED));
-		} else {
-			// The switch already exists in storage, see if anything
-			// has changed
-			sendNotificationsIfSwitchDiffers(oldSw, sw);
-		}
-	}
-
-	/**
-	 * Check if the two switches differ in their ports or in other
-	 * fields and if they differ enqueue a switch update
-	 * @param oldSw
-	 * @param newSw
-	 */
-	private synchronized void sendNotificationsIfSwitchDiffers(IOFSwitch oldSw, IOFSwitch newSw) {
-		/*TODO @Ryan Collection<PortChangeEvent> portDiffs = oldSw.comparePorts(newSw.getPorts());
-        for (PortChangeEvent ev: portDiffs) {
-            SwitchUpdate update = new SwitchUpdate(newSw.getId(),
-                                     SwitchUpdateType.PORTCHANGED,
-                                     ev.port, ev.type);
-            addUpdateToQueue(update);
-        }*/
-	}
-	
-	
-	/**
-	 * Tulio Ribeiro
-	 * @param String json
-	 * @return Map<DatapathId, OFControllerRole>
-	 */
-	private static Map<DatapathId, OFControllerRole> jsonToSwitchInitialRoleMap(String json) {
-		MappingJsonFactory f = new MappingJsonFactory();
-		JsonParser jp;
-		Map<DatapathId, OFControllerRole> retValue = new HashMap<DatapathId, OFControllerRole>();
-
-		if (json == null || json.isEmpty()) {
-			return retValue;
-		}
-
-		try {
-			try {
-				jp = f.createParser(json);
-			} catch (JsonParseException e) {
-				throw new IOException(e);
-			}
-
-			jp.nextToken();
-			if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
-				throw new IOException("Expected START_OBJECT");
-			}
-
-			while (jp.nextToken() != JsonToken.END_OBJECT) {
-				if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
-					throw new IOException("Expected FIELD_NAME");
-				}
-
-				String n = jp.getCurrentName();
-				jp.nextToken();
-				if (jp.getText().equals("")) {
-					continue;
-				}
-
-				DatapathId dpid;
-				OFControllerRole ofcr=OFControllerRole.ROLE_NOCHANGE;
-
-				try {
-					n = n.trim();
-					dpid = DatapathId.of(n);
-					ofcr = OFControllerRole.valueOf(jp.getText());
-					retValue.put(dpid, ofcr);
-
-				} catch (NumberFormatException e) {
-					log.error("Invalid DPID format: {}, or OFControllerRole: {}", n, ofcr);
-				}
-			}
-		} catch (IOException e) {
-			log.error("Problem: {}", e);
-		}
-		return retValue;
-	}
-	
-	
-}
+        /* 
+         * Get SSL config.
+         * 
+         * If a password is blank, the password field may or may not be specified.
+         * If it is specified, an empty string will be expected for blank.
+         * 
+         * The path MUST be specified if SSL is enabled.
+         */
+        Map<String, String> configParams = context.getConfigParams(this);
+        String path = configParams.get("keyStorePath");
+        String pass = configParams.get("keyStorePassword");
+        String useSsl = configParams.get("useSsl");
+
+        if (useSsl == null || path == null || path.isEmpty() || 
+                (!useSsl.equalsIgnoreCase("yes") && !useSsl.equalsIgnoreCase("true") &&
+                        !useSsl.equalsIgnoreCase("yep") && !useSsl.equalsIgnoreCase("ja") &&
+                        !useSsl.equalsIgnoreCase("stimmt")
+                        )
+                ) {
+            log.warn("SSL disabled. Using unsecure connections between Floodlight and switches.");
+            OFSwitchManager.keyStore = null;
+            OFSwitchManager.keyStorePassword = null;
+        } else {
+            log.info("SSL enabled. Using secure connections between Floodlight and switches.");
+            log.info("SSL keystore path: {}, password: {}", path, (pass == null ? "" : pass)); 
+            OFSwitchManager.keyStore = path;
+            OFSwitchManager.keyStorePassword = (pass == null ? "" : pass);
+        }
+
+        /*
+         * Get config to define what to do when a switch connects.
+         * 
+         * If a field is blank or unspecified, it will default
+         */
+        String clearInitial = configParams.get("clearTablesOnInitialHandshakeAsMaster");
+        String clearLater = configParams.get("clearTablesOnEachTransitionToMaster");
+
+        if (clearInitial == null || clearInitial.isEmpty() || 
+                (!clearInitial.equalsIgnoreCase("yes") && !clearInitial.equalsIgnoreCase("true") &&
+                        !clearInitial.equalsIgnoreCase("yep") && !clearInitial.equalsIgnoreCase("ja") &&
+                        !clearInitial.equalsIgnoreCase("stimmt"))) {
+            log.info("Clear switch flow tables on initial handshake as master: FALSE");
+            OFSwitchManager.clearTablesOnInitialConnectAsMaster = false;
+        } else {
+            log.info("Clear switch flow tables on initial handshake as master: TRUE");
+            OFSwitchManager.clearTablesOnInitialConnectAsMaster = true;
+        }
+
+        if (clearLater == null || clearLater.isEmpty() || 
+                (!clearLater.equalsIgnoreCase("yes") && !clearLater.equalsIgnoreCase("true") &&
+                        !clearLater.equalsIgnoreCase("yep") && !clearLater.equalsIgnoreCase("ja") &&
+                        !clearLater.equalsIgnoreCase("stimmt"))) {
+            log.info("Clear switch flow tables on each transition to master: FALSE");
+            OFSwitchManager.clearTablesOnEachTransitionToMaster = false;
+        } else {
+            log.info("Clear switch flow tables on each transition to master: TRUE");
+            OFSwitchManager.clearTablesOnEachTransitionToMaster = true;
+        }
+
+
+        //Define initial role per switch		
+        String switchesInitialState = configParams.get("switchesInitialState");
+        switchInitialRole = jsonToSwitchInitialRoleMap(switchesInitialState);
+        log.debug("SwitchInitialRole: {}", switchInitialRole.entrySet());
+
+        /*
+         * Get default max table for forward to controller flows. 
+         * Internal default set as class variable at top of OFSwitchManager.
+         */
+        String defaultFlowsUpToTable = configParams.get("defaultMaxTablesToReceiveTableMissFlow");
+        /* Backward compatibility */
+        if (defaultFlowsUpToTable == null || defaultFlowsUpToTable.isEmpty()) {
+            defaultFlowsUpToTable = configParams.get("defaultMaxTableToReceiveTableMissFlow");
+        }
+        if (defaultFlowsUpToTable != null && !defaultFlowsUpToTable.isEmpty()) {
+            defaultFlowsUpToTable = defaultFlowsUpToTable.toLowerCase().trim();
+            try {
+                forwardToControllerFlowsUpToTable = TableId.of(defaultFlowsUpToTable.startsWith("0x") 
+                        ? Integer.parseInt(defaultFlowsUpToTable.replaceFirst("0x", ""), 16) 
+                                : Integer.parseInt(defaultFlowsUpToTable));
+                log.info("Setting {} as the default max tables to receive table-miss flow", forwardToControllerFlowsUpToTable.toString());
+            } catch (IllegalArgumentException e) {
+                log.error("Invalid table ID {} for default max tables to receive table-miss flow. Using pre-set of {}", 
+                        defaultFlowsUpToTable, forwardToControllerFlowsUpToTable.toString());
+            }
+        } else {
+            log.info("Default max tables to receive table-miss flow not configured. Using {}", forwardToControllerFlowsUpToTable.toString());
+        }
+
+        /*
+         * Get config to define which tables per switch will get a
+         * default forward-to-controller flow. This can be used to
+         * reduce the number of such flows if only a reduced set of
+         * tables are being used.
+         */
+        String maxPerDpid = configParams.get("maxTablesToReceiveTableMissFlowPerDpid");
+        /* Backward compatibility */
+        if (maxPerDpid == null || maxPerDpid.isEmpty()) {
+            maxPerDpid = configParams.get("maxTableToReceiveTableMissFlowPerDpid");
+        }
+        forwardToControllerFlowsUpToTableByDpid = jsonToSwitchTableIdMap(maxPerDpid);
+
+        /*
+         * Get config to determine what versions of OpenFlow we will
+         * support. The versions will determine the hello's header
+         * version as well as the OF1.3.1 version bitmap contents.
+         */
+        String protocols = configParams.get("supportedOpenFlowVersions");
+        List<OFVersion> ofVersions = new ArrayList<OFVersion>();
+        if (protocols != null && !protocols.isEmpty()) {
+            protocols = protocols.toLowerCase();
+            /* 
+             * Brute-force check for all known versions. 
+             */
+            if (protocols.contains("1.0") || protocols.contains("10")) {
+                ofVersions.add(OFVersion.OF_10);
+            }
+            if (protocols.contains("1.1") || protocols.contains("11")) {
+                ofVersions.add(OFVersion.OF_11);
+            }
+            if (protocols.contains("1.2") || protocols.contains("12")) {
+                ofVersions.add(OFVersion.OF_12);
+            }
+            if (protocols.contains("1.3") || protocols.contains("13")) {
+                ofVersions.add(OFVersion.OF_13);
+            }
+            if (protocols.contains("1.4") || protocols.contains("14")) {
+                ofVersions.add(OFVersion.OF_14);
+            }
+            if (protocols.contains("1.5") || protocols.contains("15")) {
+                ofVersions.add(OFVersion.OF_15);
+            }
+            /*
+             * TODO This will need to be updated if/when 
+             * Loxi is updated to support > 1.5.
+             * 
+             * if (protocols.contains("1.6") || protocols.contains("16")) {
+             *     ofVersions.add(OFVersion.OF_16);
+             * }
+             */
+        } else {
+            log.warn("Supported OpenFlow versions not specified. Using Loxi-defined {}", OFVersion.values());
+            ofVersions.addAll(Arrays.asList(OFVersion.values()));
+        }
+
+        /* Sanity check */
+        if (ofVersions.isEmpty()) {
+            throw new IllegalStateException("OpenFlow version list should never be empty at this point. Make sure it's being populated in OFSwitchManager's init function.");
+        }
+        defaultFactory = computeInitialFactory(ofVersions);
+        ofBitmaps = computeOurVersionBitmaps(ofVersions);
+
+        log.debug("Computed OpenFlow version bitmap as {}", ofBitmaps);
+        log.info("OpenFlow version {} will be advertised to switches. Supported fallback versions {}", defaultFactory.getVersion(), ofVersions);
+
+        /* OpenFlow listen TCP port */
+        String ofPort = configParams.get("openFlowPort");
+        if (!Strings.isNullOrEmpty(ofPort)) {
+            try {
+                openFlowPort = TransportPort.of(Integer.parseInt(ofPort));
+            } catch (Exception e) {
+                log.error("Invalid OpenFlow port {}, {}", ofPort, e);
+                throw new FloodlightModuleException("Invalid OpenFlow port of " + ofPort + " in config");
+            }
+        }
+
+        /* Netty worker threads */
+        String threads = configParams.get("workerThreads");
+        if (!Strings.isNullOrEmpty(threads)) {
+            workerThreads = Integer.parseInt(threads);
+        }
+
+        /* Netty boss threads */
+        threads = configParams.get("bossThreads");
+        if (!Strings.isNullOrEmpty(threads)) {
+            bossThreads = Integer.parseInt(threads);
+        }
+
+        /* Netty TCP connection timeout */
+        String timeout = configParams.get("connectionTimeoutMs");
+        if (!Strings.isNullOrEmpty(timeout)) {
+            connectionTimeoutMsec = Integer.parseInt(timeout);
+        }
+
+        /* Netty boss thread pending connection accept backlog */
+        String backlog = configParams.get("connectionBacklog");
+        if (!Strings.isNullOrEmpty(backlog)) {
+            connectionBacklog = Integer.parseInt(backlog);
+        }
+
+        /* OpenFlow listen addresses */
+        String addresses = configParams.get("openFlowAddresses");
+        if (!Strings.isNullOrEmpty(addresses)) {
+            try {
+                openFlowAddresses = Collections.singleton(IPv4Address.of(addresses)); //TODO support list of addresses for multi-honed controllers
+            } catch (Exception e) {
+                log.error("Invalid OpenFlow address {}, {}", addresses, e);
+                throw new FloodlightModuleException("Invalid OpenFlow address of " + addresses + " in config");
+            }
+            log.debug("OpenFlow addresses set to {}", openFlowAddresses);
+        } else {
+            openFlowAddresses.add(IPv4Address.NONE);
+        }
+
+        /* OpenFlow port TCP send buffer size */
+        String tcpBuffer = configParams.get("tcpSendBufferSizeBytes");
+        if (!Strings.isNullOrEmpty(tcpBuffer)) {
+            tcpSendBufferSize = Integer.parseInt(tcpBuffer);
+        }
+
+        log.info("Listening for OpenFlow switches on {}:{}", openFlowAddresses, openFlowPort);
+        log.info("OpenFlow socket config: "
+                + "{} boss thread(s), "
+                + "{} worker thread(s), "
+                + "{} ms TCP connection timeout, "
+                + "max {} connection backlog, "
+                + "{} byte TCP send buffer size", 
+                new Object[] {
+                        bossThreads, 
+                        workerThreads, 
+                        connectionTimeoutMsec, 
+                        connectionBacklog, 
+                        tcpSendBufferSize
+                });
+    }
+
+    /**
+     * Find the max version supplied in the supported
+     * versions list and use it as the default, which
+     * will subsequently be used in our hello message
+     * header's version field.
+     * 
+     * The factory can be later "downgraded" to a lower
+     * version depending on what's computed during the
+     * version-negotiation part of the handshake.
+     * 
+     * @param ofVersions the OpenFlow versions we support
+     * @return the highest-version OFFactory we support
+     */
+    private OFFactory computeInitialFactory(List<OFVersion> ofVersions) {
+        /* This should NEVER happen. Double-checking. */
+        if (ofVersions == null || ofVersions.isEmpty()) {
+            throw new IllegalStateException("OpenFlow version list should never be null or empty at this point. Make sure it's set in the OFSwitchManager.");
+        }
+        OFVersion highest = null;
+        for (OFVersion v : ofVersions) {
+            if (highest == null) {
+                highest = v;
+            } else if (v.compareTo(highest) > 0) {
+                highest = v;
+            }
+        }
+        /* 
+         * This assumes highest != null, which
+         * it won't be if the list of versions
+         * is not empty.
+         */
+        return OFFactories.getFactory(highest);
+    }
+
+    /**
+     * Based on the list of OFVersions provided as input (or from Loxi),
+     * create a list of bitmaps for use in version negotiation during a
+     * cross-version OpenFlow handshake where both parties support 
+     * OpenFlow versions >= 1.3.1.
+     * 
+     * @param ofVersions the OpenFlow versions we support
+     * @return list of bitmaps for the versions of OpenFlow we support
+     */
+    private List<U32> computeOurVersionBitmaps(List<OFVersion> ofVersions) {
+        /* This should NEVER happen. Double-checking. */
+        if (ofVersions == null || ofVersions.isEmpty()) {
+            throw new IllegalStateException("OpenFlow version list should never be null or empty at this point. Make sure it's set in the OFSwitchManager.");
+        }
+
+        int pos = 1; /* initial bitmap in list */
+        int size = 32; /* size of a U32 */
+        int tempBitmap = 0; /* maintain the current bitmap we're working on */
+        List<U32> bitmaps = new ArrayList<U32>();
+        ArrayList<OFVersion> sortedVersions = new ArrayList<OFVersion>(ofVersions);
+        Collections.sort(sortedVersions);
+        for (OFVersion v : sortedVersions) {
+            /* Move on to another bitmap */
+            if (v.getWireVersion() > pos * size - 1 ) {
+                bitmaps.add(U32.ofRaw(tempBitmap));
+                tempBitmap = 0;
+                pos++;
+            }
+            tempBitmap = tempBitmap | (1 << (v.getWireVersion() % size));
+        }
+        if (tempBitmap != 0) {
+            bitmaps.add(U32.ofRaw(tempBitmap));
+        }
+        return bitmaps;
+    }
+
+    private static Map<DatapathId, TableId> jsonToSwitchTableIdMap(String json) {
+        MappingJsonFactory f = new MappingJsonFactory();
+        JsonParser jp;
+        Map<DatapathId, TableId> retValue = new HashMap<DatapathId, TableId>();
+
+        if (json == null || json.isEmpty()) {
+            return retValue;
+        }
+
+        try {
+            try {
+                jp = f.createParser(json);
+            } catch (JsonParseException e) {
+                throw new IOException(e);
+            }
+
+            jp.nextToken();
+            if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+                throw new IOException("Expected START_OBJECT");
+            }
+
+            while (jp.nextToken() != JsonToken.END_OBJECT) {
+                if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
+                    throw new IOException("Expected FIELD_NAME");
+                }
+
+                String n = jp.getCurrentName();
+                jp.nextToken();
+                if (jp.getText().equals("")) {
+                    continue;
+                }
+
+                DatapathId dpid;
+                try {
+                    n = n.trim();
+                    dpid = DatapathId.of(n);
+
+                    TableId tablesToGetDefaultFlow;
+                    String value = jp.getText();
+                    if (value != null && !value.isEmpty()) {
+                        value = value.trim().toLowerCase();
+                        try {
+                            tablesToGetDefaultFlow = TableId.of(
+                                    value.startsWith("0x") 
+                                    ? Integer.parseInt(value.replaceFirst("0x", ""), 16) 
+                                            : Integer.parseInt(value)
+                                    ); /* will throw exception if outside valid TableId number range */
+                            retValue.put(dpid, tablesToGetDefaultFlow);
+                            log.debug("Setting max tables to receive table-miss flow to {} for DPID {}", 
+                                    tablesToGetDefaultFlow.toString(), dpid.toString());
+                        } catch (IllegalArgumentException e) { /* catches both IllegalArgumentExcpt. and NumberFormatExcpt. */
+                            log.error("Invalid value of {} for max tables to receive table-miss flow for DPID {}. Using default of {}.", value, dpid.toString());
+                        }
+                    }
+                } catch (NumberFormatException e) {
+                    log.error("Invalid DPID format {} for max tables to receive table-miss flow for specific DPID. Using default for the intended DPID.", n);
+                }
+            }
+        } catch (IOException e) {
+            log.error("Using default for remaining DPIDs. JSON formatting error in max tables to receive table-miss flow for DPID input String: {}", e);
+        }
+        return retValue;
+    }
+
+    @Override
+    public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
+        startUpBase(context);
+        bootstrapNetty();
+    }
+
+    /**
+     * Startup method that includes everything besides the netty boostrap.
+     * This has been isolated for testing.
+     * @param context floodlight module context
+     * @throws FloodlightModuleException
+     */
+    public void startUpBase(FloodlightModuleContext context) throws FloodlightModuleException {
+        // Initial Role
+        role = floodlightProvider.getRole().getOFRole();
+
+        // IRoleListener
+        floodlightProvider.addHAListener(this);
+
+        loadLogicalCategories();
+    }
+
+    /**
+     * Bootstraps netty, the server that handles all openflow connections
+     */
+    public void bootstrapNetty() {
+        try {
+            bossGroup = new NioEventLoopGroup(bossThreads);
+            workerGroup = new NioEventLoopGroup(workerThreads);
+
+            ServerBootstrap bootstrap = new ServerBootstrap()
+                    .group(bossGroup, workerGroup)
+                    .channel(NioServerSocketChannel.class)
+                    .option(ChannelOption.SO_REUSEADDR, true)
+                    .option(ChannelOption.SO_KEEPALIVE, true)
+                    .option(ChannelOption.TCP_NODELAY, true)
+                    .option(ChannelOption.SO_SNDBUF, tcpSendBufferSize)
+                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeoutMsec)
+                    .option(ChannelOption.SO_BACKLOG, connectionBacklog);
+
+            OFChannelInitializer initializer = new OFChannelInitializer(
+                    this, 
+                    this, 
+                    debugCounterService, 
+                    timer, 
+                    ofBitmaps, 
+                    defaultFactory, 
+                    keyStore, 
+                    keyStorePassword);
+
+            bootstrap.childHandler(initializer);
+
+            cg = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+            Set<InetSocketAddress> addrs = new HashSet<InetSocketAddress>();
+            if (openFlowAddresses.isEmpty()) {
+                cg.add(bootstrap.bind(new InetSocketAddress(InetAddress.getByAddress(IPv4Address.NONE.getBytes()), openFlowPort.getPort())).channel());
+            } else {
+                for (IPv4Address ip : openFlowAddresses) {
+                    addrs.add(new InetSocketAddress(InetAddress.getByAddress(ip.getBytes()), openFlowPort.getPort()));
+                }
+            }
+
+            for (InetSocketAddress sa : addrs) {
+                cg.add(bootstrap.bind(sa).channel());
+                log.debug("Listening for switch connections on {}", sa);
+            }
+
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Performs startup related actions for logical OF message categories.
+     * Setting the categories list to immutable ensures that unsupported operation
+     * exceptions will be activated if modifications are attempted.
+     */
+    public void loadLogicalCategories() {
+        logicalOFMessageCategories = ImmutableSet.copyOf(logicalOFMessageCategories);
+        numRequiredConnections = calcNumRequiredConnections();
+    }
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    @Override
+    public boolean isCallbackOrderingPrereq(HAListenerTypeMarker type, String name) {
+        return false;
+    }
+
+    @Override
+    public boolean isCallbackOrderingPostreq(HAListenerTypeMarker type, String name) {
+        return false;
+    }
+
+    @Override
+    public void controllerNodeIPsChanged(Map<String, String> curControllerNodeIPs,
+            Map<String, String> addedControllerNodeIPs,
+            Map<String, String> removedControllerNodeIPs) {		
+    }
+
+    @Override
+    public void keysModified(Iterator<DatapathId> keys, UpdateType type) {
+        if (type == UpdateType.LOCAL) {
+            // We only care for remote updates
+            return;
+        }
+        while(keys.hasNext()) {
+            DatapathId key = keys.next();
+            Versioned<SwitchSyncRepresentation> versionedSwitch = null;
+            try {
+                versionedSwitch = storeClient.get(key);
+            } catch (SyncException e) {
+                log.error("Exception while retrieving switch " + key.toString() +
+                        " from sync store. Skipping", e);
+                continue;
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("Reveiced switch store notification: key={}, " +
+                        "entry={}", key, versionedSwitch.getValue());
+            }
+            // versionedSwtich won't be null. storeClient.get() always
+            // returns a non-null or throws an exception
+            if (versionedSwitch.getValue() == null) {
+                switchRemovedFromStore(key);
+                continue;
+            }
+            SwitchSyncRepresentation storedSwitch = versionedSwitch.getValue();
+            IOFSwitch sw = getSwitch(storedSwitch.getDpid());
+            //TODO need to get IOFSwitchBackend setFeaturesReply(storedSwitch.getFeaturesReply(sw.getOFFactory()));
+            if (!key.equals(storedSwitch.getFeaturesReply(sw.getOFFactory()).getDatapathId())) {
+                log.error("Inconsistent DPIDs from switch sync store: " +
+                        "key is {} but sw.getId() says {}. Ignoring",
+                        key.toString(), sw.getId());
+                continue;
+            }
+            switchAddedToStore(sw);
+        }
+    }
+
+    /**
+     * Called when we receive a store notification about a switch that
+     * has been removed from the sync store
+     * @param dpid
+     */
+    private synchronized void switchRemovedFromStore(DatapathId dpid) {
+        if (floodlightProvider.getRole() != HARole.STANDBY) {
+            return; // only read from store if slave
+        }
+        IOFSwitch oldSw = syncedSwitches.remove(dpid);
+        if (oldSw != null) {
+            addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED));
+        }
+    }
+
+    /**
+     * Called when we receive a store notification about a new or updated
+     * switch.
+     * @param sw
+     */
+    private synchronized void switchAddedToStore(IOFSwitch sw) {
+        if (floodlightProvider.getRole() != HARole.STANDBY) {
+            return; // only read from store if slave
+        }
+        DatapathId dpid = sw.getId();
+
+        IOFSwitch oldSw = syncedSwitches.put(dpid, sw);
+        if (oldSw == null)  {
+            addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.ADDED));
+        } else {
+            // The switch already exists in storage, see if anything
+            // has changed
+            sendNotificationsIfSwitchDiffers(oldSw, sw);
+        }
+    }
+
+    /**
+     * Check if the two switches differ in their ports or in other
+     * fields and if they differ enqueue a switch update
+     * @param oldSw
+     * @param newSw
+     */
+    private synchronized void sendNotificationsIfSwitchDiffers(IOFSwitch oldSw, IOFSwitch newSw) {
+        for (OFPortDesc oldPort : oldSw.getPorts()) {
+            if (newSw.getPort(oldPort.getPortNo()) == null) { /* delete */
+                SwitchUpdate update = new SwitchUpdate(newSw.getId(),
+                        SwitchUpdateType.PORTCHANGED,
+                        oldPort, PortChangeType.DELETE);
+                addUpdateToQueue(update);
+            } else { /* in common; some form of update */
+                OFPortDesc newPort = newSw.getPort(oldPort.getPortNo());
+                if (newPort.getState().contains(OFPortState.LINK_DOWN) && /* went down */
+                        !oldPort.getState().contains(OFPortState.LINK_DOWN)) {
+                    SwitchUpdate update = new SwitchUpdate(newSw.getId(),
+                            SwitchUpdateType.PORTCHANGED,
+                            newPort, PortChangeType.DOWN);
+                    addUpdateToQueue(update);
+                } else if (newPort.getState().contains(OFPortState.LIVE) && /* went up */
+                        !oldPort.getState().contains(OFPortState.LIVE)) {
+                    SwitchUpdate update = new SwitchUpdate(newSw.getId(),
+                            SwitchUpdateType.PORTCHANGED,
+                            newPort, PortChangeType.UP);
+                    addUpdateToQueue(update);
+                } else if (!newPort.equals(oldPort)) {
+                    SwitchUpdate update = new SwitchUpdate(newSw.getId(),
+                            SwitchUpdateType.PORTCHANGED,
+                            newPort, PortChangeType.OTHER_UPDATE);
+                    addUpdateToQueue(update);
+                }
+            }
+        }
+        for (OFPortDesc newPort : newSw.getPorts()) {
+            if (oldSw.getPort(newPort.getPortNo()) == null) { /* add */
+                SwitchUpdate update = new SwitchUpdate(newSw.getId(),
+                        SwitchUpdateType.PORTCHANGED,
+                        newPort, PortChangeType.ADD);
+                addUpdateToQueue(update);
+            }
+        }
+    }
+
+
+    /**
+     * Tulio Ribeiro
+     * @param String json
+     * @return Map<DatapathId, OFControllerRole>
+     */
+    private static Map<DatapathId, OFControllerRole> jsonToSwitchInitialRoleMap(String json) {
+        MappingJsonFactory f = new MappingJsonFactory();
+        JsonParser jp;
+        Map<DatapathId, OFControllerRole> retValue = new HashMap<DatapathId, OFControllerRole>();
+
+        if (json == null || json.isEmpty()) {
+            return retValue;
+        }
+
+        try {
+            try {
+                jp = f.createParser(json);
+            } catch (JsonParseException e) {
+                throw new IOException(e);
+            }
+
+            jp.nextToken();
+            if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+                throw new IOException("Expected START_OBJECT");
+            }
+
+            while (jp.nextToken() != JsonToken.END_OBJECT) {
+                if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
+                    throw new IOException("Expected FIELD_NAME");
+                }
+
+                String n = jp.getCurrentName();
+                jp.nextToken();
+                if (jp.getText().equals("")) {
+                    continue;
+                }
+
+                DatapathId dpid;
+                OFControllerRole ofcr=OFControllerRole.ROLE_NOCHANGE;
+
+                try {
+                    n = n.trim();
+                    dpid = DatapathId.of(n);
+                    ofcr = OFControllerRole.valueOf(jp.getText());
+                    retValue.put(dpid, ofcr);
+
+                } catch (NumberFormatException e) {
+                    log.error("Invalid DPID format: {}, or OFControllerRole: {}", n, ofcr);
+                }
+            }
+        } catch (IOException e) {
+            log.error("Problem: {}", e);
+        }
+        return retValue;
+    }
+
+    @Override
+    public void addSwitchEvent(DatapathId switchDpid, String reason, boolean flushNow) {}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/internal/RoleManager.java b/src/main/java/net/floodlightcontroller/core/internal/RoleManager.java
index 017aeb68121fd921565770e85554573913ac5770..de73d0cf3292edb0f834d58917ee1850943def8a 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/RoleManager.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/RoleManager.java
@@ -202,7 +202,7 @@ public class RoleManager {
                 log.debug("Dispatching HA Role update newRole = {}",
                           newRole);
             }
-            for (IHAListener listener : controller.haListeners.getOrderedListeners()) {
+            for (IHAListener listener : Controller.haListeners.getOrderedListeners()) {
                 if (log.isTraceEnabled()) {
                     log.trace("Calling HAListener {} with transitionTo{}",
                               listener.getName(), newRole);
diff --git a/src/main/java/net/floodlightcontroller/core/internal/ShutdownServiceImpl.java b/src/main/java/net/floodlightcontroller/core/internal/ShutdownServiceImpl.java
index 0c1c8c6d25b28fd4bf5c3fb9c0e85399bd95ce6c..faf39528cc647b0f88c769fd2ce45589e8f4adaf 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/ShutdownServiceImpl.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/ShutdownServiceImpl.java
@@ -77,7 +77,6 @@ public class ShutdownServiceImpl implements IFloodlightModule,
         shutdownListeners.add(listener);
     }
 
-    @SuppressFBWarnings(value="DM_EXIT", justification="exit by design")
     @Override
     public void terminate(@Nullable final String reason, final int exitCode) {
         final String paddedReason;
@@ -125,7 +124,6 @@ public class ShutdownServiceImpl implements IFloodlightModule,
         System.exit(exitCode);
     }
 
-    @SuppressFBWarnings(value="DM_EXIT", justification="exit by design")
     @Override
     public void terminate(final String reason, final Throwable e, final int exitCode) {
         final String paddedReason;
@@ -165,6 +163,5 @@ public class ShutdownServiceImpl implements IFloodlightModule,
         logger.error("**************************************************");
         // Game Over.
         System.exit(exitCode);
-
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/internal/SwitchEvent.java b/src/main/java/net/floodlightcontroller/core/internal/SwitchEvent.java
deleted file mode 100644
index 7a4f36b37cc54e5c9c5d7698ffc04b9ac7adba2d..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/core/internal/SwitchEvent.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.floodlightcontroller.core.internal;
-
-import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType;
-import org.projectfloodlight.openflow.types.DatapathId;
-
-public class SwitchEvent {
-    @EventColumn(name = "dpid", description = EventFieldType.DPID)
-    DatapathId dpid;
-
-    @EventColumn(name = "reason", description = EventFieldType.STRING)
-    String reason;
-
-    public SwitchEvent(DatapathId dpid, String reason) {
-        this.dpid = dpid;
-        this.reason = reason;
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/rest/AggregateStatistics.java b/src/main/java/net/floodlightcontroller/core/rest/AggregateStatistics.java
deleted file mode 100644
index 3d0fe680e544ffa5a7701ad42f0037fe8de9037c..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/core/rest/AggregateStatistics.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package net.floodlightcontroller.core.rest;
-
-import org.projectfloodlight.openflow.protocol.OFAggregateStatsReply;
-import org.projectfloodlight.openflow.types.U64;
-
-public class AggregateStatistics {
-
-    private final U64 packetCount;
-    private final U64 byteCount;
-    private final long flowCount;
-
-    private AggregateStatistics(U64 packetCount, U64 byteCount, long flowCount) {
-        this.packetCount = packetCount;
-        this.byteCount = byteCount;
-        this.flowCount = flowCount;
-    }
-
-    public static AggregateStatistics of(U64 packetCount, U64 byteCount,
-            long flowCount) {
-        return new AggregateStatistics(packetCount, byteCount, flowCount);
-    }
-
-    public static AggregateStatistics of(OFAggregateStatsReply statsReply) {
-        return new AggregateStatistics(statsReply.getPacketCount(),
-                statsReply.getByteCount(), statsReply.getFlowCount());
-    }
-
-    public U64 getPacketCount() {
-        return packetCount;
-    }
-
-    public U64 getByteCount() {
-        return byteCount;
-    }
-
-    public long getFlowCount() {
-        return flowCount;
-    }
-}
diff --git a/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java b/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java
deleted file mode 100644
index 7cb96bcee1f84545bcf4069d4aba091503d73cce..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package net.floodlightcontroller.core.rest;
-
-import java.net.SocketAddress;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Map;
-import java.util.Set;
-
-import javax.annotation.Nonnull;
-
-import net.floodlightcontroller.core.IOFConnection;
-import net.floodlightcontroller.core.IOFSwitch;
-import net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler;
-import org.projectfloodlight.openflow.protocol.OFCapabilities;
-import org.projectfloodlight.openflow.protocol.OFPortDesc;
-import org.projectfloodlight.openflow.types.DatapathId;
-
-import com.google.common.base.Preconditions;
-
-/**
- * REST representation of an OF Switch. Stitches together data from different
- * areas of the platform to provide a complete, centralized representation.
- * @author Jason Parraga <Jason.Parraga@bigswitch.com>
- *
- */
-public class SwitchRepresentation {
-
-    private final long buffers;
-    private final Set<OFCapabilities> capabilities;
-    private final Short tables;
-    private final SocketAddress inetAddress;
-    private final Collection<OFPortDesc> sortedPorts;
-    private final boolean isConnected;
-    private final Date connectedSince;
-    private final DatapathId dpid;
-    private final Map<Object, Object> attributes;
-    private final boolean isActive;
-
-    private final Collection<IOFConnection> connections;
-    private final String handshakeState;
-    private final String quarantineReason;
-
-    public SwitchRepresentation(@Nonnull IOFSwitch sw, @Nonnull OFSwitchHandshakeHandler handshakeHandler) {
-        Preconditions.checkNotNull(sw, "switch must not be null");
-        Preconditions.checkNotNull(handshakeHandler, "handshakeHandler must not be null");
-
-        // IOFSwitch
-        this.buffers = sw.getBuffers();
-        this.capabilities = sw.getCapabilities();
-        this.tables = sw.getNumTables();
-        this.inetAddress = sw.getInetAddress();
-        this.sortedPorts = sw.getSortedPorts();
-        this.isConnected = sw.isConnected();
-        this.connectedSince = sw.getConnectedSince();
-        this.dpid = sw.getId();
-        this.attributes = sw.getAttributes();
-        this.isActive = sw.isActive();
-
-        // OFSwitchHandshakeHandler
-        this.connections = handshakeHandler.getConnections();
-        this.handshakeState = handshakeHandler.getState();
-        this.quarantineReason = handshakeHandler.getQuarantineReason();
-    }
-
-    public long getBuffers() {
-        return this.buffers;
-    }
-
-    public Short getTables() {
-        return this.tables;
-    }
-
-    public Set<OFCapabilities> getCapabilities() {
-        return this.capabilities;
-    }
-
-    public SocketAddress getInetAddress() {
-        return this.inetAddress;
-    }
-
-    public Collection<OFPortDesc> getSortedPorts() {
-        return this.sortedPorts;
-    }
-
-    public boolean isConnected() {
-        return this.isConnected;
-    }
-
-    public Date getConnectedSince() {
-        return this.connectedSince;
-    }
-
-    public DatapathId getDpid() {
-        return this.dpid;
-    }
-
-    public Map<Object, Object> getAttributes() {
-        return this.attributes;
-    }
-
-    public boolean isActive() {
-        return this.isActive;
-    }
-
-    public Collection<IOFConnection> getConnections() {
-        return this.connections;
-    }
-
-    public String getHandshakeState() {
-        return this.handshakeState;
-    }
-
-    public String getQuarantineReason() {
-        return this.quarantineReason;
-    }
-}
diff --git a/src/main/java/net/floodlightcontroller/core/rest/TableStatistics.java b/src/main/java/net/floodlightcontroller/core/rest/TableStatistics.java
deleted file mode 100644
index 480ff2004e0eae6f133fb79a94adc318b6b57cf4..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/core/rest/TableStatistics.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package net.floodlightcontroller.core.rest;
-
-import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
-import org.projectfloodlight.openflow.types.TableId;
-import org.projectfloodlight.openflow.types.U64;
-
-public class TableStatistics {
-
-    private final TableId tableId;
-    private final long activeCount;
-    private final U64 lookupCount;
-    private final U64 matchedCount;
-
-    private TableStatistics(TableId tableId, long activeCount, U64 lookupCount, U64 matchedCount) {
-        this.tableId = tableId;
-        this.activeCount = activeCount;
-        this.lookupCount = lookupCount;
-        this.matchedCount = matchedCount;
-    }
-
-    public static TableStatistics of(TableId tableId, long activeCount, U64 lookupCount, U64 matchedCount) {
-        return new TableStatistics(tableId, activeCount, lookupCount, matchedCount);
-    }
-
-    public static TableStatistics of(OFTableStatsEntry entry) {
-        return new TableStatistics(entry.getTableId(),
-                entry.getActiveCount(), entry.getLookupCount(), entry.getMatchedCount());
-    }
-
-    public TableId getTableId() {
-        return tableId;
-    }
-
-    public long getActiveCount() {
-        return activeCount;
-    }
-
-    public U64 getLookupCount() {
-        return lookupCount;
-    }
-
-    public U64 getMatchedCount() {
-        return matchedCount;
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/web/AllSwitchStatisticsResource.java b/src/main/java/net/floodlightcontroller/core/web/AllSwitchStatisticsResource.java
index 3a7624943f3b8a6b166d42499832fc04cb624dfc..de4d09dc39f3fb20435ff56474aa17c0bcc682a7 100644
--- a/src/main/java/net/floodlightcontroller/core/web/AllSwitchStatisticsResource.java
+++ b/src/main/java/net/floodlightcontroller/core/web/AllSwitchStatisticsResource.java
@@ -68,10 +68,22 @@ public class AllSwitchStatisticsResource extends SwitchResourceBase {
 			type = OFStatsType.QUEUE;
 			rType = REQUESTTYPE.OFSTATS;
 			break;
+		case OFStatsTypeStrings.QUEUE_DESC:
+			type = OFStatsType.QUEUE_DESC;
+			rType = REQUESTTYPE.OFSTATS;
+			break;
 		case OFStatsTypeStrings.FLOW:
 			type = OFStatsType.FLOW;
 			rType = REQUESTTYPE.OFSTATS;
 			break;
+		case OFStatsTypeStrings.FLOW_LIGHTWEIGHT:
+			type = OFStatsType.FLOW_LIGHTWEIGHT;
+			rType = REQUESTTYPE.OFSTATS;
+			break;
+		case OFStatsTypeStrings.FLOW_MONITOR:
+			type = OFStatsType.FLOW_MONITOR;
+			rType = REQUESTTYPE.OFSTATS;
+			break;
 		case OFStatsTypeStrings.AGGREGATE:
 			type = OFStatsType.AGGREGATE;
 			rType = REQUESTTYPE.OFSTATS;
@@ -88,7 +100,10 @@ public class AllSwitchStatisticsResource extends SwitchResourceBase {
 			type = OFStatsType.TABLE_FEATURES;
 			rType = REQUESTTYPE.OFSTATS;
 			break;
-
+		case OFStatsTypeStrings.TABLE_DESC:
+			type = OFStatsType.TABLE_DESC;
+			rType = REQUESTTYPE.OFSTATS;
+			break;
 		case OFStatsTypeStrings.GROUP:
 			type = OFStatsType.GROUP;
 			rType = REQUESTTYPE.OFSTATS;
@@ -116,7 +131,16 @@ public class AllSwitchStatisticsResource extends SwitchResourceBase {
 		case OFStatsTypeStrings.FEATURES:
 			rType = REQUESTTYPE.OFFEATURES;
 			break;
+		case OFStatsTypeStrings.BUNDLE_FEATURES:
+			type = OFStatsType.BUNDLE_FEATURES;
+			rType = REQUESTTYPE.OFSTATS;
+			break;
+		case OFStatsTypeStrings.CONTROLLER_STATUS:
+			type = OFStatsType.CONTROLLER_STATUS;
+			rType = REQUESTTYPE.OFSTATS;
+			break;
 		default:
+		    model.put("error", new StatsReply()); // will generate error message when serializer is invoked
 			return model;
 		}
 
diff --git a/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java
index 423b8c1f6de9421fa78ba0d9733573db250dcf36..f0e65cfeef86d255d60bd612b104f88079ee51d5 100644
--- a/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java
+++ b/src/main/java/net/floodlightcontroller/core/web/ControllerSwitchesResource.java
@@ -23,6 +23,7 @@ import java.util.HashSet;
 import net.floodlightcontroller.core.internal.IOFSwitchService;
 import net.floodlightcontroller.core.IOFSwitch;
 
+import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.restlet.resource.Get;
 import org.restlet.resource.ServerResource;
@@ -40,37 +41,45 @@ public class ControllerSwitchesResource extends ServerResource {
         private final DatapathId dpid;
         private final String inetAddress; 
         private final long connectedSince;
-        public DatapathIDJsonSerializerWrapper(DatapathId dpid, String inetAddress, long connectedSince) {
+        private final String version;
+        public DatapathIDJsonSerializerWrapper(DatapathId dpid, String inetAddress, long connectedSince, OFVersion version) {
             this.dpid = dpid;
             this.inetAddress = inetAddress;
             this.connectedSince = connectedSince;
+            this.version = version.toString();
         }
         
         @JsonSerialize(using=DPIDSerializer.class)
         public DatapathId getSwitchDPID() {
             return dpid;
         }
+        
         public String getInetAddress() {
             return inetAddress;
         }
+        
         public long getConnectedSince() {
             return connectedSince;
         }
-
-
-
+        
+        public String getOpenFlowVersion() {
+            return version;
+        }
     }
 
     @Get("json")
-    public Set<DatapathIDJsonSerializerWrapper> retrieve(){
+    public Set<DatapathIDJsonSerializerWrapper> retrieve() {
         IOFSwitchService switchService = 
             (IOFSwitchService) getContext().getAttributes().
                 get(IOFSwitchService.class.getCanonicalName());
         Set<DatapathIDJsonSerializerWrapper> dpidSets = new HashSet<DatapathIDJsonSerializerWrapper>();
-        for(IOFSwitch sw: switchService.getAllSwitchMap().values()) {
-            dpidSets.add(new DatapathIDJsonSerializerWrapper(sw.getId(), sw.getInetAddress().toString(),  sw.getConnectedSince().getTime()));
+        for (IOFSwitch sw: switchService.getAllSwitchMap().values()) {
+            dpidSets.add(new DatapathIDJsonSerializerWrapper(sw.getId(), 
+                    sw.getInetAddress().toString(), 
+                    sw.getConnectedSince().getTime(),
+                    sw.getOFFactory().getVersion()));
 
         }
         return dpidSets;
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/web/OFStatsTypeStrings.java b/src/main/java/net/floodlightcontroller/core/web/OFStatsTypeStrings.java
index 38fdb945d9c6efa6f7947c142f5161300053a2d4..afa17ab2c3145587496967b70217254f0687d576 100644
--- a/src/main/java/net/floodlightcontroller/core/web/OFStatsTypeStrings.java
+++ b/src/main/java/net/floodlightcontroller/core/web/OFStatsTypeStrings.java
@@ -5,6 +5,7 @@ public class OFStatsTypeStrings {
 	public static final String DESC = "desc";
 	public static final String EXPERIMENTER = "experimenter";
 	public static final String FLOW = "flow";
+	public static final String FLOW_LIGHTWEIGHT = "flow-lightweight";
 	public static final String GROUP = "group";
 	public static final String GROUP_DESC = "group-desc";
 	public static final String GROUP_FEATURES = "group-features";
@@ -14,8 +15,12 @@ public class OFStatsTypeStrings {
 	public static final String PORT = "port";
 	public static final String PORT_DESC = "port-desc";
 	public static final String QUEUE = "queue";
+	public static final String QUEUE_DESC = "queue-desc";
 	public static final String TABLE = "table";
+	public static final String TABLE_DESC = "table-desc";
 	public static final String TABLE_FEATURES = "table-features";
-	public static final String FEATURES = "features"; // TODO not a stats but a features request
-													// not sure where to put it though
-}
+	public static final String FLOW_MONITOR = "flow-monitor";
+	public static final String CONTROLLER_STATUS = "controller-status";
+	public static final String BUNDLE_FEATURES = "bundle-features";
+	public static final String FEATURES = "features"; /* Not a stats type, technically */
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java b/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java
index 956d720e8b8bd803c35831d80ae3b1a2104df07d..b7003b3c41c6067647698260a0ec904bbb192fda 100644
--- a/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java
+++ b/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java
@@ -28,6 +28,7 @@ import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.protocol.ver13.OFMeterSerializerVer13;
 import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFGroup;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.TableId;
 import org.projectfloodlight.openflow.protocol.OFFeaturesRequest;
@@ -49,177 +50,219 @@ import com.google.common.util.concurrent.ListenableFuture;
  *
  */
 public class SwitchResourceBase extends ServerResource {
-	protected static Logger log = LoggerFactory.getLogger(SwitchResourceBase.class);
-
-	public enum REQUESTTYPE {
-		OFSTATS,
-		OFFEATURES
-	}
-
-	@Override
-	protected void doInit() throws ResourceException {
-		super.doInit();
-
-	}
-
-	/**
-	 * Use for requests that originate from the REST server that use their context to get a
-	 * reference to the switch service.
-	 * @param switchId
-	 * @param statType
-	 * @return
-	 */
-	@SuppressWarnings("unchecked")
-	protected List<OFStatsReply> getSwitchStatistics(DatapathId switchId,
-			OFStatsType statType) {
-		IOFSwitchService switchService = (IOFSwitchService) getContext().getAttributes().get(IOFSwitchService.class.getCanonicalName());
-
-		IOFSwitch sw = switchService.getSwitch(switchId);
-		ListenableFuture<?> future;
-		List<OFStatsReply> values = null;
-		Match match;
-		if (sw != null) {
-			OFStatsRequest<?> req = null;
-			switch (statType) {
-			case FLOW:
-				match = sw.getOFFactory().buildMatch().build();
-				req = sw.getOFFactory().buildFlowStatsRequest()
-						.setMatch(match)
-						.setOutPort(OFPort.ANY)
-						.setTableId(TableId.ALL)
-						.build();
-				break;
-			case AGGREGATE:
-				match = sw.getOFFactory().buildMatch().build();
-				req = sw.getOFFactory().buildAggregateStatsRequest()
-						.setMatch(match)
-						.setOutPort(OFPort.ANY)
-						.setTableId(TableId.ALL)
-						.build();
-				break;
-			case PORT:
-				req = sw.getOFFactory().buildPortStatsRequest()
-				.setPortNo(OFPort.ANY)
-				.build();
-				break;
-			case QUEUE:
-				req = sw.getOFFactory().buildQueueStatsRequest()
-				.setPortNo(OFPort.ANY)
-				.setQueueId(UnsignedLong.MAX_VALUE.longValue())
-				.build();
-				break;
-			case DESC:
-				// pass - nothing todo besides set the type above
-				req = sw.getOFFactory().buildDescStatsRequest()
-				.build();
-				break;
-			case GROUP:
-				if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) > 0) {
-					req = sw.getOFFactory().buildGroupStatsRequest()				
-							.build();
-				}
-				break;
-
-			case METER:
-				if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
-					req = sw.getOFFactory().buildMeterStatsRequest()
-							.setMeterId(OFMeterSerializerVer13.ALL_VAL)
-							.build();
-				}
-				break;
-
-			case GROUP_DESC:			
-				if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) > 0) {
-					req = sw.getOFFactory().buildGroupDescStatsRequest()			
-							.build();
-				}
-				break;
-
-			case GROUP_FEATURES:
-				if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) > 0) {
-					req = sw.getOFFactory().buildGroupFeaturesStatsRequest()
-							.build();
-				}
-				break;
-
-			case METER_CONFIG:
-				if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
-					req = sw.getOFFactory().buildMeterConfigStatsRequest()
-							.setMeterId(0xffFFffFF)
-							.build();
-				}
-				break;
-
-			case METER_FEATURES:
-				if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
-					req = sw.getOFFactory().buildMeterFeaturesStatsRequest()
-							.build();
-				}
-				break;
-
-			case TABLE:
-				if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) > 0) {
-					req = sw.getOFFactory().buildTableStatsRequest()
-							.build();
-				}
-				break;
-
-			case TABLE_FEATURES:	
-				if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) > 0) {
-					req = sw.getOFFactory().buildTableFeaturesStatsRequest()
-							.build();		
-				}
-				break;
-			case PORT_DESC:
-				if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
-					req = sw.getOFFactory().buildPortDescStatsRequest()
-							.build();
-				}
-				break;
-			case EXPERIMENTER: //TODO @Ryan support new OF1.1+ stats types			
-			default:
-				log.error("Stats Request Type {} not implemented yet", statType.name());
-				break;
-			}
-
-			try {
-				if (req != null) {
-					future = sw.writeStatsRequest(req);
-					values = (List<OFStatsReply>) future.get(10, TimeUnit.SECONDS);
-				}
-			} catch (Exception e) {
-				log.error("Failure retrieving statistics from switch " + sw, e);
-			}
-		}
-		return values;
-	}
-
-	protected List<OFStatsReply> getSwitchStatistics(String switchId, OFStatsType statType) {
-		return getSwitchStatistics(DatapathId.of(switchId), statType);
-	}
-
-	protected OFFeaturesReply getSwitchFeaturesReply(DatapathId switchId) {
-		IOFSwitchService switchService =
-				(IOFSwitchService) getContext().getAttributes().
-				get(IOFSwitchService.class.getCanonicalName());
-
-		IOFSwitch sw = switchService.getSwitch(switchId);
-		Future<OFFeaturesReply> future;
-		OFFeaturesReply featuresReply = null;
-		OFFeaturesRequest featuresRequest = sw.getOFFactory().buildFeaturesRequest().build();
-		if (sw != null) {
-			try {
-				future = sw.writeRequest(featuresRequest);
-				featuresReply = future.get(10, TimeUnit.SECONDS);
-			} catch (Exception e) {
-				log.error("Failure getting features reply from switch" + sw, e);
-			}
-		}
-
-		return featuresReply;
-	}
-
-	protected OFFeaturesReply getSwitchFeaturesReply(String switchId) {
-		return getSwitchFeaturesReply(DatapathId.of(switchId));
-	}	
+    protected static Logger log = LoggerFactory.getLogger(SwitchResourceBase.class);
+
+    public enum REQUESTTYPE {
+        OFSTATS,
+        OFFEATURES
+    }
+
+    @Override
+    protected void doInit() throws ResourceException {
+        super.doInit();
+
+    }
+
+    /**
+     * Use for requests that originate from the REST server that use their context to get a
+     * reference to the switch service.
+     * @param switchId
+     * @param statType
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    protected List<OFStatsReply> getSwitchStatistics(DatapathId switchId,
+            OFStatsType statType) {
+        IOFSwitchService switchService = (IOFSwitchService) getContext().getAttributes().get(IOFSwitchService.class.getCanonicalName());
+
+        IOFSwitch sw = switchService.getSwitch(switchId);
+        ListenableFuture<?> future;
+        List<OFStatsReply> values = null;
+        Match match;
+        if (sw != null) {
+            OFStatsRequest<?> req = null;
+            switch (statType) {
+            case FLOW:
+                match = sw.getOFFactory().buildMatch().build();
+                req = sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) == 0 ? 
+                        sw.getOFFactory().buildFlowStatsRequest()
+                        .setMatch(match)
+                        .setOutPort(OFPort.ANY)
+                        .setTableId(TableId.ALL)
+                        .build() :
+                            sw.getOFFactory().buildFlowStatsRequest()
+                            .setMatch(match)
+                            .setOutPort(OFPort.ANY)
+                            .setTableId(TableId.ALL)
+                            .setOutGroup(OFGroup.ANY)
+                            .build();
+                break;
+            case AGGREGATE:
+                match = sw.getOFFactory().buildMatch().build();
+                req = sw.getOFFactory().buildAggregateStatsRequest()
+                        .setMatch(match)
+                        .setOutPort(OFPort.ANY)
+                        .setTableId(TableId.ALL)
+                        .build();
+                break;
+            case PORT:
+                req = sw.getOFFactory().buildPortStatsRequest()
+                .setPortNo(OFPort.ANY)
+                .build();
+                break;
+            case QUEUE:
+                req = sw.getOFFactory().buildQueueStatsRequest()
+                .setPortNo(OFPort.ANY)
+                .setQueueId(UnsignedLong.MAX_VALUE.longValue())
+                .build();
+                break;
+            case DESC:
+                // pass - nothing todo besides set the type above
+                req = sw.getOFFactory().buildDescStatsRequest()
+                .build();
+                break;
+            case GROUP:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) > 0) {
+                    req = sw.getOFFactory().buildGroupStatsRequest()				
+                            .build();
+                }
+                break;
+            case METER:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
+                    req = sw.getOFFactory().buildMeterStatsRequest()
+                            .setMeterId(OFMeterSerializerVer13.ALL_VAL)
+                            .build();
+                }
+                break;
+            case GROUP_DESC:			
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) > 0) {
+                    req = sw.getOFFactory().buildGroupDescStatsRequest()			
+                            .build();
+                }
+                break;
+            case GROUP_FEATURES:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) > 0) {
+                    req = sw.getOFFactory().buildGroupFeaturesStatsRequest()
+                            .build();
+                }
+                break;
+            case METER_CONFIG:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
+                    req = sw.getOFFactory().buildMeterConfigStatsRequest()
+                            .setMeterId(0xffFFffFF)
+                            .build();
+                }
+                break;
+            case METER_FEATURES:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
+                    req = sw.getOFFactory().buildMeterFeaturesStatsRequest()
+                            .build();
+                }
+                break;
+            case TABLE:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) > 0) {
+                    req = sw.getOFFactory().buildTableStatsRequest()
+                            .build();
+                }
+                break;
+            case TABLE_FEATURES:	
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) > 0) {
+                    req = sw.getOFFactory().buildTableFeaturesStatsRequest()
+                            .build();		
+                }
+                break;
+            case PORT_DESC:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) {
+                    req = sw.getOFFactory().buildPortDescStatsRequest()
+                            .build();
+                }
+                break;
+            case EXPERIMENTER:		
+                log.error("Stats Request Type {} not implemented yet", statType.name());
+                break;
+            case BUNDLE_FEATURES:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_15) >= 0) {
+                    req = sw.getOFFactory().buildBundleFeaturesStatsRequest()
+                            .build();
+                }
+                break;
+            case CONTROLLER_STATUS:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_15) >= 0) {
+                    req = sw.getOFFactory().buildControllerStatusStatsRequest()
+                            .build();
+                }
+                break;
+            case FLOW_LIGHTWEIGHT:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_15) >= 0) {	
+                    match = sw.getOFFactory().buildMatch().build();
+                    req = sw.getOFFactory().buildFlowLightweightStatsRequest()
+                            .setMatch(match)
+                            .setOutPort(OFPort.ANY)
+                            .setTableId(TableId.ALL)
+                            .build();
+                }
+                break;
+            case FLOW_MONITOR:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_14) >= 0) {
+                    req = sw.getOFFactory().buildFlowMonitorRequest()
+                            .build();
+                }
+                break;
+            case QUEUE_DESC:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_14) >= 0) {
+                    req = sw.getOFFactory().buildQueueDescStatsRequest()
+                            .setPortNo(OFPort.ANY)
+                            .setQueueId(0xffFFffFF) /* all queues */
+                            .build();
+                }
+                break;
+            case TABLE_DESC:
+                if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_14) >= 0) {
+                    req = sw.getOFFactory().buildTableDescStatsRequest()
+                            .build();
+                }
+                break;
+                /* omit a default so we will know (via warning) if we miss one in the future */
+            }
+
+            try {
+                if (req != null) {
+                    future = sw.writeStatsRequest(req);
+                    values = (List<OFStatsReply>) future.get(10, TimeUnit.SECONDS);
+                }
+            } catch (Exception e) {
+                log.error("Failure retrieving statistics from switch " + sw, e);
+            }
+        }
+        return values;
+    }
+
+    protected List<OFStatsReply> getSwitchStatistics(String switchId, OFStatsType statType) {
+        return getSwitchStatistics(DatapathId.of(switchId), statType);
+    }
+
+    protected OFFeaturesReply getSwitchFeaturesReply(DatapathId switchId) {
+        IOFSwitchService switchService =
+                (IOFSwitchService) getContext().getAttributes().
+                get(IOFSwitchService.class.getCanonicalName());
+
+        IOFSwitch sw = switchService.getSwitch(switchId);
+        Future<OFFeaturesReply> future;
+        OFFeaturesReply featuresReply = null;
+        OFFeaturesRequest featuresRequest = sw.getOFFactory().buildFeaturesRequest().build();
+        if (sw != null) {
+            try {
+                future = sw.writeRequest(featuresRequest);
+                featuresReply = future.get(10, TimeUnit.SECONDS);
+            } catch (Exception e) {
+                log.error("Failure getting features reply from switch" + sw, e);
+            }
+        }
+
+        return featuresReply;
+    }
+
+    protected OFFeaturesReply getSwitchFeaturesReply(String switchId) {
+        return getSwitchFeaturesReply(DatapathId.of(switchId));
+    }	
 }
diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchStatisticsResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchStatisticsResource.java
index 89733f1d15715cb083363c2e5ece4a1bf9bbc2cf..2bd14872e1fba44913fd01d1a5013ec63880c26f 100644
--- a/src/main/java/net/floodlightcontroller/core/web/SwitchStatisticsResource.java
+++ b/src/main/java/net/floodlightcontroller/core/web/SwitchStatisticsResource.java
@@ -61,14 +61,30 @@ public class SwitchStatisticsResource extends SwitchResourceBase {
 				values = getSwitchStatistics(switchId, OFStatsType.PORT);
 				result.setStatType(OFStatsType.PORT);
 				break;
+            case OFStatsTypeStrings.PORT_DESC:
+                values = getSwitchStatistics(switchId, OFStatsType.PORT_DESC);
+                result.setStatType(OFStatsType.PORT_DESC);
+                break;
 			case OFStatsTypeStrings.QUEUE:
 				values = getSwitchStatistics(switchId, OFStatsType.QUEUE);
 				result.setStatType(OFStatsType.QUEUE);
 				break;
+			case OFStatsTypeStrings.QUEUE_DESC:
+                values = getSwitchStatistics(switchId, OFStatsType.QUEUE_DESC);
+                result.setStatType(OFStatsType.QUEUE_DESC);
+                break;
 			case OFStatsTypeStrings.FLOW:
 				values = getSwitchStatistics(switchId, OFStatsType.FLOW);
 				result.setStatType(OFStatsType.FLOW);
 				break;
+			case OFStatsTypeStrings.FLOW_LIGHTWEIGHT:
+                values = getSwitchStatistics(switchId, OFStatsType.FLOW_LIGHTWEIGHT);
+                result.setStatType(OFStatsType.FLOW_LIGHTWEIGHT);
+                break;
+			case OFStatsTypeStrings.FLOW_MONITOR:
+                values = getSwitchStatistics(switchId, OFStatsType.FLOW_MONITOR);
+                result.setStatType(OFStatsType.FLOW_MONITOR);
+                break;
 			case OFStatsTypeStrings.AGGREGATE:
 				values = getSwitchStatistics(switchId, OFStatsType.AGGREGATE);
 				result.setStatType(OFStatsType.AGGREGATE);
@@ -77,10 +93,6 @@ public class SwitchStatisticsResource extends SwitchResourceBase {
 				values = getSwitchStatistics(switchId, OFStatsType.DESC);
 				result.setStatType(OFStatsType.DESC);
 				break;			
-			case OFStatsTypeStrings.PORT_DESC:
-				values = getSwitchStatistics(switchId, OFStatsType.PORT_DESC);
-				result.setStatType(OFStatsType.PORT_DESC);
-				break;
 			case OFStatsTypeStrings.GROUP:
 				values = getSwitchStatistics(switchId, OFStatsType.GROUP);
 				result.setStatType(OFStatsType.GROUP);
@@ -109,6 +121,10 @@ public class SwitchStatisticsResource extends SwitchResourceBase {
 				values = getSwitchStatistics(switchId, OFStatsType.TABLE);
 				result.setStatType(OFStatsType.TABLE);
 				break;
+			case OFStatsTypeStrings.TABLE_DESC:
+                values = getSwitchStatistics(switchId, OFStatsType.TABLE_DESC);
+                result.setStatType(OFStatsType.TABLE_DESC);
+                break;
 			case OFStatsTypeStrings.TABLE_FEATURES:
 				values = getSwitchStatistics(switchId, OFStatsType.TABLE_FEATURES);
 				result.setStatType(OFStatsType.TABLE_FEATURES);
@@ -117,6 +133,14 @@ public class SwitchStatisticsResource extends SwitchResourceBase {
 				values = getSwitchFeaturesReply(switchId);
 				result.setStatType(OFStatsType.EXPERIMENTER);
 				break;
+			case OFStatsTypeStrings.BUNDLE_FEATURES:
+                values = getSwitchStatistics(switchId, OFStatsType.BUNDLE_FEATURES);
+                result.setStatType(OFStatsType.BUNDLE_FEATURES);
+                break;
+			case OFStatsTypeStrings.CONTROLLER_STATUS:
+                values = getSwitchStatistics(switchId, OFStatsType.CONTROLLER_STATUS);
+                result.setStatType(OFStatsType.CONTROLLER_STATUS);
+                break;  
 			case OFStatsTypeStrings.FEATURES:
 				values = getSwitchFeaturesReply(switchId);
 				result.setStatType(null); // we will assume anything in "values" with a null stattype is "features"
@@ -126,7 +150,7 @@ public class SwitchStatisticsResource extends SwitchResourceBase {
 			}
 		} else {
 			log.error("Invalid or disconnected switch {}", switchIdStr);
-			// if there was an error, the serializer will 
+			// if there was an error, the serializer will report it
 		}
 		
 		result.setDatapathId(switchId);
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/DPIDSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/DPIDSerializer.java
index 7e6bdf772a1f9b9c39a86ed81266330c9ec50324..efb8b7cef06f0736fa066fed5aeb074868ffd27d 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/DPIDSerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/DPIDSerializer.java
@@ -35,5 +35,4 @@ public class DPIDSerializer extends JsonSerializer<DatapathId> {
                                   throws IOException, JsonProcessingException {
         jGen.writeString(dpid.toString());
     }
-
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/MatchSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/MatchSerializer.java
index 9bc587ced4742c47e9bfc1f8a7472406ca334605..0ef75c6d512c14aa483e87e7235a968eacbaa255 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/MatchSerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/MatchSerializer.java
@@ -42,129 +42,11 @@ public class MatchSerializer extends JsonSerializer<Match> {
 		jGen.writeObjectFieldStart("match");
 		Iterator<MatchField<?>> mi = match.getMatchFields().iterator(); // get iter to any match field type
 		Match m = match;
-
 		while (mi.hasNext()) {
 			MatchField<?> mf = mi.next();
-			switch (mf.id) {
-			case IN_PORT:
-				jGen.writeStringField(MatchUtils.STR_IN_PORT, matchValueToString(m, mf));
-				break;
-			case IN_PHY_PORT:
-				jGen.writeStringField(MatchUtils.STR_IN_PHYS_PORT, matchValueToString(m, mf));
-				break;
-			case ARP_OP:
-				jGen.writeStringField(MatchUtils.STR_ARP_OPCODE, matchValueToString(m, mf));
-				break;
-			case ARP_SHA:
-				jGen.writeStringField(MatchUtils.STR_ARP_SHA, matchValueToString(m, mf));
-				break;
-			case ARP_SPA:
-				jGen.writeStringField(MatchUtils.STR_ARP_SPA, matchValueToString(m, mf));
-				break;
-			case ARP_THA:
-				jGen.writeStringField(MatchUtils.STR_ARP_DHA, matchValueToString(m, mf));
-				break;
-			case ARP_TPA:
-				jGen.writeStringField(MatchUtils.STR_ARP_DPA, matchValueToString(m, mf));
-				break;
-			case ETH_TYPE: // TODO Remove this "0x" when Loxigen is updated.
-				jGen.writeStringField(MatchUtils.STR_DL_TYPE, m.isPartiallyMasked(mf) ?
-						"0x" + m.getMasked(mf).toString() : "0x" + m.get(mf).toString());
-				break;
-			case ETH_SRC:
-				jGen.writeStringField(MatchUtils.STR_DL_SRC, matchValueToString(m, mf));
-				break;
-			case ETH_DST:
-				jGen.writeStringField(MatchUtils.STR_DL_DST, matchValueToString(m, mf));
-				break;
-			case VLAN_VID:
-				jGen.writeStringField(MatchUtils.STR_DL_VLAN, matchValueToString(m, mf));
-				break;
-			case VLAN_PCP:
-				jGen.writeStringField(MatchUtils.STR_DL_VLAN_PCP, matchValueToString(m, mf));
-				break;
-			case ICMPV4_TYPE:
-				jGen.writeStringField(MatchUtils.STR_ICMP_TYPE, matchValueToString(m, mf));
-				break;
-			case ICMPV4_CODE:
-				jGen.writeStringField(MatchUtils.STR_ICMP_CODE, matchValueToString(m, mf));
-				break;
-			case ICMPV6_TYPE:
-				jGen.writeStringField(MatchUtils.STR_ICMPV6_TYPE, matchValueToString(m, mf));
-				break;
-			case ICMPV6_CODE:
-				jGen.writeStringField(MatchUtils.STR_ICMPV6_CODE, matchValueToString(m, mf));
-				break;
-			case IP_DSCP:
-				jGen.writeStringField(MatchUtils.STR_NW_DSCP, matchValueToString(m, mf));
-				break;
-			case IP_ECN:
-				jGen.writeStringField(MatchUtils.STR_NW_ECN, matchValueToString(m, mf));
-				break;
-			case IP_PROTO:
-				jGen.writeStringField(MatchUtils.STR_NW_PROTO, matchValueToString(m, mf));
-				break;
-			case IPV4_SRC:
-				jGen.writeStringField(MatchUtils.STR_NW_SRC, matchValueToString(m, mf));
-				break;
-			case IPV4_DST:
-				jGen.writeStringField(MatchUtils.STR_NW_DST, matchValueToString(m, mf));
-				break;
-			case IPV6_SRC:
-				jGen.writeStringField(MatchUtils.STR_IPV6_SRC, matchValueToString(m, mf));
-				break;
-			case IPV6_DST:
-				jGen.writeStringField(MatchUtils.STR_IPV6_DST, matchValueToString(m, mf));
-				break;
-			case IPV6_FLABEL:
-				jGen.writeStringField(MatchUtils.STR_IPV6_FLOW_LABEL, matchValueToString(m, mf));
-				break;
-			case IPV6_ND_SLL:
-				jGen.writeStringField(MatchUtils.STR_IPV6_ND_SSL, matchValueToString(m, mf));
-				break;
-			case IPV6_ND_TARGET:
-				jGen.writeStringField(MatchUtils.STR_IPV6_ND_TARGET, matchValueToString(m, mf));
-				break;
-			case IPV6_ND_TLL:
-				jGen.writeStringField(MatchUtils.STR_IPV6_ND_TTL, matchValueToString(m, mf));
-				break;
-			case METADATA:
-				jGen.writeStringField(MatchUtils.STR_METADATA, matchValueToString(m, mf));
-				break;
-			case MPLS_LABEL:
-				jGen.writeStringField(MatchUtils.STR_MPLS_LABEL, matchValueToString(m, mf));
-				break;
-			case MPLS_TC:
-				jGen.writeStringField(MatchUtils.STR_MPLS_TC, matchValueToString(m, mf));
-				break;
-			case MPLS_BOS:
-				jGen.writeStringField(MatchUtils.STR_MPLS_BOS, matchValueToString(m, mf));
-				break;
-			case SCTP_SRC:
-				jGen.writeStringField(MatchUtils.STR_SCTP_SRC, matchValueToString(m, mf));
-				break;
-			case SCTP_DST:
-				jGen.writeStringField(MatchUtils.STR_SCTP_DST, matchValueToString(m, mf));
-				break;
-			case TCP_SRC:
-				jGen.writeStringField(MatchUtils.STR_TCP_SRC, matchValueToString(m, mf));
-				break;
-			case TCP_DST:
-				jGen.writeStringField(MatchUtils.STR_TCP_DST, matchValueToString(m, mf));
-				break;
-			case UDP_SRC:
-				jGen.writeStringField(MatchUtils.STR_UDP_SRC, matchValueToString(m, mf));
-				break;
-			case UDP_DST:
-				jGen.writeStringField(MatchUtils.STR_UDP_DST, matchValueToString(m, mf));
-				break;
-			default:
-				// either a BSN or unknown match type
-				break;
-			} // end switch of match type
-		} // end while over non-wildcarded matches
+			jGen.writeStringField(MatchUtils.getMatchFieldName(mf.id), matchValueToString(m, mf));
+		}
 
 		jGen.writeEndObject(); // end match
 	}
-}
-
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java
index e0cbf88e0f94e4c6efdefc08c85a7d9ee6ba538f..b577be126dc518c8e089ab97835ce5359671f633 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java
@@ -4,68 +4,8 @@ import java.io.IOException;
 import java.util.List;
 
 import net.floodlightcontroller.util.ActionUtils;
-import net.floodlightcontroller.util.MatchUtils;
 
 import org.projectfloodlight.openflow.protocol.action.OFAction;
-import org.projectfloodlight.openflow.protocol.action.OFActionEnqueue;
-import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
-import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
-import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
-import org.projectfloodlight.openflow.protocol.action.OFActionPopMpls;
-import org.projectfloodlight.openflow.protocol.action.OFActionPushMpls;
-import org.projectfloodlight.openflow.protocol.action.OFActionPushPbb;
-import org.projectfloodlight.openflow.protocol.action.OFActionPushVlan;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetField;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetMplsLabel;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetMplsTc;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetMplsTtl;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetNwEcn;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetNwTos;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetNwTtl;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetQueue;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetTpDst;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetTpSrc;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpOp;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpSha;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpSpa;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpTha;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpTpa;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthDst;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthSrc;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthType;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv4Code;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv4Type;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv6Code;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv6Type;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpDscp;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpEcn;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpProto;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Dst;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Src;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Dst;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Flabel;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdSll;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdTarget;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdTll;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Src;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmMetadata;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsBos;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsLabel;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsTc;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmSctpDst;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmSctpSrc;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmTcpDst;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmTcpSrc;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmUdpDst;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmUdpSrc;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanPcp;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanVid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -103,199 +43,10 @@ public class OFActionListSerializer extends JsonSerializer<List<OFAction>> {
      * @throws JsonProcessingException
      */
     public static void serializeActions(JsonGenerator jsonGenerator, List<OFAction> actions) throws IOException, JsonProcessingException {
-        StringBuilder sb = new StringBuilder();
-        int len = actions.size();
-        int pos = 0;
-        for (OFAction a : actions) {
-            switch (a.getType()) {
-            case OUTPUT:
-                sb.append(ActionUtils.STR_OUTPUT).append("=").append(((OFActionOutput)a).getPort().toString());
-                break;
-            /* begin OF1.0 ONLY actions */
-            case SET_VLAN_VID:
-                sb.append(ActionUtils.STR_VLAN_SET_VID).append("=").append(((OFActionSetVlanVid)a).getVlanVid().getVlan());
-                break;
-            case SET_VLAN_PCP:
-                sb.append(ActionUtils.STR_VLAN_SET_PCP).append("=").append(((OFActionSetVlanPcp)a).getVlanPcp().getValue());
-                break;
-            case SET_QUEUE:
-                sb.append(ActionUtils.STR_QUEUE_SET).append("=").append(((OFActionSetQueue)a).getQueueId());
-                break;
-            case SET_DL_SRC:
-                sb.append(ActionUtils.STR_DL_SRC_SET).append("=").append(((OFActionSetDlSrc)a).getDlAddr().toString());
-                break;
-            case SET_DL_DST:
-                sb.append(ActionUtils.STR_DL_DST_SET).append("=").append(((OFActionSetDlDst)a).getDlAddr().toString());
-                break;
-            case SET_NW_SRC:
-                sb.append(ActionUtils.STR_NW_SRC_SET).append("=").append(((OFActionSetNwSrc)a).getNwAddr().toString());
-                break;
-            case SET_NW_DST:
-                sb.append(ActionUtils.STR_NW_DST_SET).append("=").append(((OFActionSetNwDst)a).getNwAddr().toString());
-                break;
-            case SET_NW_TOS:
-            	sb.append(ActionUtils.STR_NW_TOS_SET).append("=").append(((OFActionSetNwTos)a).getNwTos());
-                break;    
-            case SET_TP_SRC:
-            	sb.append(ActionUtils.STR_TP_SRC_SET).append("=").append(((OFActionSetTpSrc)a).getTpPort().getPort());
-                break;
-            case SET_TP_DST:
-            	sb.append(ActionUtils.STR_TP_DST_SET).append("=").append(((OFActionSetTpDst)a).getTpPort().getPort());
-                break;
-            /* end OF1.0 ONLY actions; begin OF1.1+ actions */
-            case ENQUEUE:
-                sb.append(ActionUtils.STR_ENQUEUE).append("=").append(((OFActionEnqueue)a).getPort().getPortNumber());
-                break;
-            case GROUP:
-            	sb.append(ActionUtils.STR_GROUP).append("=").append(((OFActionGroup)a).getGroup().toString());
-                break;
-            case STRIP_VLAN:
-            	sb.append(ActionUtils.STR_VLAN_STRIP).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
-                break;
-            case PUSH_VLAN:
-            	sb.append(ActionUtils.STR_VLAN_PUSH).append("=").append(((OFActionPushVlan)a).getEthertype().getValue());
-                break;
-            case PUSH_MPLS:
-            	sb.append(ActionUtils.STR_MPLS_PUSH).append("=").append(((OFActionPushMpls)a).getEthertype().getValue());
-                break;
-            case PUSH_PBB:
-            	sb.append(ActionUtils.STR_PBB_PUSH).append("=").append(((OFActionPushPbb)a).getEthertype().getValue());
-                break;
-            case POP_VLAN:
-            	sb.append(ActionUtils.STR_VLAN_POP).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
-                break;
-            case POP_MPLS:
-            	sb.append(ActionUtils.STR_MPLS_POP).append("=").append(((OFActionPopMpls)a).getEthertype().getValue());
-                break;
-            case POP_PBB:
-            	sb.append(ActionUtils.STR_PBB_POP).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
-                break;
-            case COPY_TTL_IN:
-            	sb.append(ActionUtils.STR_TTL_IN_COPY).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
-                break;
-            case COPY_TTL_OUT:
-            	sb.append(ActionUtils.STR_TTL_OUT_COPY).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
-                break;
-            case DEC_NW_TTL:
-            	sb.append(ActionUtils.STR_NW_TTL_DEC).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
-                break;
-            case DEC_MPLS_TTL:
-            	sb.append(ActionUtils.STR_MPLS_TTL_DEC).append("=").append(ActionUtils.STR_NOT_APPLICABLE);
-                break;
-            case SET_MPLS_LABEL:
-            	sb.append(ActionUtils.STR_MPLS_LABEL_SET).append("=").append(((OFActionSetMplsLabel)a).getMplsLabel());
-                break;
-            case SET_MPLS_TC:
-            	sb.append(ActionUtils.STR_MPLS_TC_SET).append("=").append(((OFActionSetMplsTc)a).getMplsTc());
-                break;
-            case SET_MPLS_TTL:
-                sb.append(ActionUtils.STR_MPLS_TTL_SET).append("=").append(((OFActionSetMplsTtl)a).getMplsTtl());
-                break;
-            case SET_NW_ECN:
-            	sb.append(ActionUtils.STR_NW_ECN_SET).append("=").append(((OFActionSetNwEcn)a).getNwEcn().getEcnValue());
-                break;
-            case SET_NW_TTL:
-            	sb.append(ActionUtils.STR_NW_TTL_SET).append("=").append(((OFActionSetNwTtl)a).getNwTtl());
-                break;
-            case EXPERIMENTER:
-            	sb.append(ActionUtils.STR_EXPERIMENTER).append("=").append(((OFActionExperimenter)a).getExperimenter());
-                break;
-            case SET_FIELD:
-                if (((OFActionSetField)a).getField() instanceof OFOxmArpOp) {
-                	sb.append(MatchUtils.STR_ARP_OPCODE).append("=").append(((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode());
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSha) {
-                	sb.append(MatchUtils.STR_ARP_SHA).append("=").append(((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTha) {
-                	sb.append(MatchUtils.STR_ARP_DHA).append("=").append(((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString());
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSpa) {
-                	sb.append(MatchUtils.STR_ARP_SPA).append("=").append(((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTpa) {
-                	sb.append(MatchUtils.STR_ARP_DPA).append("=").append(((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdSll) {                		
-                	sb.append(MatchUtils.STR_IPV6_ND_SSL).append("=").append(((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString());
-            	} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTll) {                		
-            		sb.append(MatchUtils.STR_IPV6_ND_TTL).append("=").append(((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString());
-            	} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTarget) {                		
-            		sb.append(MatchUtils.STR_IPV6_ND_TARGET).append("=").append(((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); 
-            	}
-                /* DATA LAYER */
-                else if (((OFActionSetField)a).getField() instanceof OFOxmEthType) {
-                	sb.append(MatchUtils.STR_DL_TYPE).append("=").append(((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue());
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmEthSrc) {
-                	sb.append(MatchUtils.STR_DL_SRC).append("=").append(((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString());
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmEthDst) {
-                	sb.append(MatchUtils.STR_DL_DST).append("=").append(((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanVid) {
-                	sb.append(MatchUtils.STR_DL_VLAN).append("=").append(((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanPcp) {
-                } 
-                /* ICMP */
-                else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Code) {
-                	sb.append(MatchUtils.STR_ICMP_CODE).append("=").append(((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Type) {
-                	sb.append(MatchUtils.STR_ICMP_TYPE).append("=").append(((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Code) {                		
-                	sb.append(MatchUtils.STR_ICMPV6_CODE).append("=").append(((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw()); 
-            	}  else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Type) {                		
-            		sb.append(MatchUtils.STR_ICMPV6_TYPE).append("=").append(((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw()); 
-            	}
-                /* NETWORK LAYER */
-                else if (((OFActionSetField)a).getField() instanceof OFOxmIpProto) {
-                	sb.append(MatchUtils.STR_NW_PROTO).append("=").append(((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Src) {
-                	sb.append(MatchUtils.STR_NW_SRC).append("=").append(((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Dst) {
-                	sb.append(MatchUtils.STR_NW_DST).append("=").append(((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Src) {                		
-                	sb.append(MatchUtils.STR_IPV6_SRC).append("=").append(((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); 
-            	} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Dst) {                		
-            		sb.append(MatchUtils.STR_IPV6_DST).append("=").append(((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
-            	} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Flabel) {                		
-            		sb.append(MatchUtils.STR_IPV6_FLOW_LABEL).append("=").append(((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); 
-            	} else if (((OFActionSetField)a).getField() instanceof OFOxmIpEcn) {
-            		sb.append(MatchUtils.STR_NW_ECN).append("=").append(((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpDscp) {
-                	sb.append(MatchUtils.STR_NW_DSCP).append("=").append(((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue()); 
-                } 
-                /* TRANSPORT LAYER, TCP, UDP, and SCTP */
-                else if (((OFActionSetField)a).getField() instanceof OFOxmTcpSrc) {
-                	sb.append(MatchUtils.STR_TCP_SRC).append("=").append(((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpDst) {
-                	sb.append(MatchUtils.STR_TCP_DST).append("=").append(((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpSrc) {
-                	sb.append(MatchUtils.STR_UDP_SRC).append("=").append(((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpDst) {
-                	sb.append(MatchUtils.STR_UDP_DST).append("=").append(((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpSrc) {
-                	sb.append(MatchUtils.STR_SCTP_SRC).append("=").append(((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpDst) {
-                	sb.append(MatchUtils.STR_SCTP_DST).append("=").append(((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort()); 
-                }
-                /* MPLS */
-                else if (((OFActionSetField)a).getField() instanceof OFOxmMplsLabel) {
-                	sb.append(MatchUtils.STR_MPLS_LABEL).append("=").append(((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsTc) {
-                	sb.append(MatchUtils.STR_MPLS_TC).append("=").append(((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue()); 
-                } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsBos) {
-                	sb.append(MatchUtils.STR_MPLS_TC).append("=").append(((OFOxmMplsBos) ((OFActionSetField) a).getField()).getValue().toString()); 
-                } 
-                /* METADATA */
-                else if (((OFActionSetField)a).getField() instanceof OFOxmMetadata) {
-                	sb.append(MatchUtils.STR_METADATA).append("=").append(((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue()); 
-                } else {
-                    logger.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a));
-                }
-            } // end switch over action type
-            pos++;
-            if (pos < len) {
-            	sb.append(",");
-            }
-        } // end for over all actions
-        
         if (actions.isEmpty()) {
             jsonGenerator.writeStringField("none", "drop");
         } else {
-        	jsonGenerator.writeStringField("actions", sb.toString());
+        	jsonGenerator.writeStringField("actions", ActionUtils.actionsToString(actions)); //TODO update to write each action in JSON
         }
-    } // end method
-}
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OFFlowModSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OFFlowModSerializer.java
index a7a6bd2680280c9733792da29f327789e2b63bdf..3ac52e2c40b1d57f69efe2d8b3e7dabfc69310b0 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/OFFlowModSerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/OFFlowModSerializer.java
@@ -43,18 +43,18 @@ import org.slf4j.LoggerFactory;
  * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
  */
 public class OFFlowModSerializer extends JsonSerializer<OFFlowMod> {
-    protected static Logger logger = LoggerFactory.getLogger(OFFlowModSerializer.class);
+	protected static Logger logger = LoggerFactory.getLogger(OFFlowModSerializer.class);
 
 	@Override
 	public void serialize(OFFlowMod fm, JsonGenerator jGen, SerializerProvider serializer)
 			throws IOException, JsonProcessingException {
-		
+		serializeFlowMod(jGen, fm);
 	}
 
 	public static void serializeFlowMod(JsonGenerator jGen, OFFlowMod flowMod) throws IOException, JsonProcessingException {
-		
-        jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); // IMHO this just looks nicer and is easier to read if everything is quoted
-		
+
+		jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); // IMHO this just looks nicer and is easier to read if everything is quoted
+
 		jGen.writeStartObject();
 		jGen.writeStringField("version", flowMod.getVersion().toString()); // return the enum names
 		jGen.writeStringField("command", flowMod.getCommand().toString());
@@ -90,8 +90,14 @@ public class OFFlowModSerializer extends JsonSerializer<OFFlowMod> {
 			jGen.writeStringField("outGroup", flowMod.getOutGroup().toString());
 			jGen.writeStringField("tableId", flowMod.getTableId().toString());
 			break;
+		case OF_15:
+			jGen.writeNumberField("flags", OFFlowModFlagsSerializerVer14.toWireValue(flowMod.getFlags()));
+			jGen.writeNumberField("cookieMask", flowMod.getCookieMask().getValue());
+			jGen.writeStringField("outGroup", flowMod.getOutGroup().toString());
+			jGen.writeStringField("tableId", flowMod.getTableId().toString());
+			break;
 		default:
-			logger.error("Could not decode OFVersion {}", flowMod.getVersion());
+			logger.error("Unimplemented serializer for OFVersion {}", flowMod.getVersion());
 			break;
 		}
 
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OFGroupModSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OFGroupModSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c144a17c8f708e9897f119b53b063d7cc832473
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/OFGroupModSerializer.java
@@ -0,0 +1,83 @@
+package net.floodlightcontroller.core.web.serializers;
+
+/**
+ *    Copyright 2011,2012 Big Switch Networks, Inc. 
+ *    Originally created by David Erickson, Stanford University
+ * 
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonGenerator.Feature;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import net.floodlightcontroller.util.GroupUtils;
+
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Serialize any OFGroupMod in JSON.
+ * 
+ * Use automatically by Jackson via JsonSerialize(using=OFGroupModSerializer.class),
+ * or use the static function within this class within another serializer.
+ * 
+ * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
+ */
+public class OFGroupModSerializer extends JsonSerializer<OFGroupMod> {
+    protected static Logger logger = LoggerFactory.getLogger(OFGroupModSerializer.class);
+
+	@Override
+	public void serialize(OFGroupMod gm, JsonGenerator jGen, SerializerProvider serializer)
+			throws IOException, JsonProcessingException {
+		serializeGroupMod(jGen, gm);
+	}
+
+	public static void serializeGroupMod(JsonGenerator jGen, OFGroupMod groupMod) throws IOException, JsonProcessingException {
+		
+        jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); // IMHO this just looks nicer and is easier to read if everything is quoted
+		
+		jGen.writeStartObject();
+		jGen.writeStringField("version", groupMod.getVersion().toString()); // return the enum names
+		jGen.writeStringField("command", groupMod.getCommand().toString());
+		jGen.writeNumberField("group_number", groupMod.getGroup().getGroupNumber());
+		switch (groupMod.getGroupType()) {
+		case ALL:
+			jGen.writeStringField(GroupUtils.GROUP_TYPE, GroupUtils.GROUP_TYPE_ALL);
+			break;
+		case FF:
+			jGen.writeStringField(GroupUtils.GROUP_TYPE, GroupUtils.GROUP_TYPE_FF);
+			break;
+		case INDIRECT:
+			jGen.writeStringField(GroupUtils.GROUP_TYPE, GroupUtils.GROUP_TYPE_INDIRECT);
+			break;
+		case SELECT:
+			jGen.writeStringField(GroupUtils.GROUP_TYPE, GroupUtils.GROUP_TYPE_SELECT);
+			break;
+		default:
+			logger.error("Omitting unknown group type {}", groupMod.getGroupType());
+			break;
+		}
+		jGen.writeStringField("version", groupMod.getVersion().toString());
+		jGen.writeNumberField("xid", groupMod.getXid());
+		jGen.writeFieldName(GroupUtils.GROUP_BUCKETS);
+		GroupUtils.groupBucketsToJsonArray(jGen, groupMod.getBuckets());
+		jGen.writeEndObject();
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OFInstructionListSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OFInstructionListSerializer.java
index 0388461c0b97b0474c20e864fa82e53373d768cb..5d43ba6e87204601d0146200038ebc0ae6db2760 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/OFInstructionListSerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/OFInstructionListSerializer.java
@@ -10,6 +10,7 @@ import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyAct
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionExperimenter;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionMeter;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionStatTrigger;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteActions;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteMetadata;
 
@@ -68,9 +69,13 @@ public class OFInstructionListSerializer extends JsonSerializer<List<OFInstructi
                 case WRITE_ACTIONS:
                     jGen.writeObjectFieldStart(InstructionUtils.STR_WRITE_ACTIONS);
                     OFActionListSerializer.serializeActions(jGen, ((OFInstructionWriteActions)i).getActions());
-                default:
-                    // shouldn't ever get here
-                    break;
+				case DEPRECATED:
+                    jGen.writeObjectFieldStart(InstructionUtils.STR_DEPRECATED);
+					break;
+				case STAT_TRIGGER:
+					jGen.writeFieldName(InstructionUtils.STR_STAT_TRIGGER);
+					InstructionUtils.statTriggerToJsonString(jGen, (OFInstructionStatTrigger)i);
+					break;
                 } // end switch on instruction
                 jGen.writeEndObject(); // end specific instruction
             } // end for instructions
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
index 2042ae58b28d2e51d5b5638d1f75c4e619b60fa3..570120300720b8ef0a3bc0353cdd7f0b129b69bc 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
@@ -18,7 +18,10 @@
 package net.floodlightcontroller.core.web.serializers;
 
 import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonGenerator.Feature;
@@ -28,14 +31,25 @@ import com.fasterxml.jackson.databind.SerializerProvider;
 
 import net.floodlightcontroller.core.web.OFStatsTypeStrings;
 import net.floodlightcontroller.core.web.StatsReply;
+import net.floodlightcontroller.util.OXMUtils;
 
 import org.projectfloodlight.openflow.protocol.OFActionType;
 import org.projectfloodlight.openflow.protocol.OFBucket;
 import org.projectfloodlight.openflow.protocol.OFBucketCounter;
+import org.projectfloodlight.openflow.protocol.OFBundleFeatureFlags;
+import org.projectfloodlight.openflow.protocol.OFBundleFeaturesStatsReply;
+import org.projectfloodlight.openflow.protocol.OFControllerStatusEntry;
+import org.projectfloodlight.openflow.protocol.OFControllerStatusStatsReply;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsReply;
+import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
+import org.projectfloodlight.openflow.protocol.OFFlowMonitorReply;
+import org.projectfloodlight.openflow.protocol.OFFlowMonitorReplyEntry;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFGroupCapabilities;
 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFGroupFeaturesStatsReply;
@@ -46,15 +60,34 @@ import org.projectfloodlight.openflow.protocol.OFMeterConfig;
 import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply;
 import org.projectfloodlight.openflow.protocol.OFMeterFeatures;
 import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
+import org.projectfloodlight.openflow.protocol.OFMeterFlags;
 import org.projectfloodlight.openflow.protocol.OFMeterStats;
 import org.projectfloodlight.openflow.protocol.OFMeterStatsReply;
+import org.projectfloodlight.openflow.protocol.OFPortConfig;
+import org.projectfloodlight.openflow.protocol.OFPortDescProp;
+import org.projectfloodlight.openflow.protocol.OFPortFeatures;
+import org.projectfloodlight.openflow.protocol.OFPortState;
 import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
+import org.projectfloodlight.openflow.protocol.OFQueueDesc;
+import org.projectfloodlight.openflow.protocol.OFQueueDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFQueueStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFQueueStatsReply;
 import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFPortStatsProp;
+import org.projectfloodlight.openflow.protocol.OFPortStatsPropEthernet;
+import org.projectfloodlight.openflow.protocol.OFPortStatsPropExperimenter;
+import org.projectfloodlight.openflow.protocol.OFPortStatsPropOptical;
 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
+import org.projectfloodlight.openflow.protocol.OFTableConfig;
+import org.projectfloodlight.openflow.protocol.OFTableDesc;
+import org.projectfloodlight.openflow.protocol.OFTableDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFTableFeatureProp;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropApplyActions;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropApplyActionsMiss;
+import org.projectfloodlight.openflow.protocol.OFTableFeaturePropApplyCopyfield;
+import org.projectfloodlight.openflow.protocol.OFTableFeaturePropApplyCopyfieldMiss;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropApplySetfield;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropApplySetfieldMiss;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropExperimenter;
@@ -64,9 +97,13 @@ import org.projectfloodlight.openflow.protocol.OFTableFeaturePropInstructionsMis
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropMatch;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropNextTables;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropNextTablesMiss;
+import org.projectfloodlight.openflow.protocol.OFTableFeaturePropOxmValues;
+import org.projectfloodlight.openflow.protocol.OFTableFeaturePropTableSyncFrom;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWildcards;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWriteActions;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWriteActionsMiss;
+import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWriteCopyfield;
+import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWriteCopyfieldMiss;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWriteSetfield;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturePropWriteSetfieldMiss;
 import org.projectfloodlight.openflow.protocol.OFTableFeatures;
@@ -79,33 +116,15 @@ import org.projectfloodlight.openflow.protocol.meterband.OFMeterBand;
 import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDrop;
 import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDscpRemark;
 import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandExperimenter;
-import org.projectfloodlight.openflow.protocol.ver13.OFFlowModFlagsSerializerVer13;
-import org.projectfloodlight.openflow.protocol.ver13.OFMeterBandTypeSerializerVer13;
-// Use Loxigen's serializer
-import org.projectfloodlight.openflow.protocol.ver13.OFPortFeaturesSerializerVer13;
-import org.projectfloodlight.openflow.protocol.ver13.OFStatsReplyFlagsSerializerVer13;
-import org.projectfloodlight.openflow.protocol.ver13.OFTableFeaturePropTypeSerializerVer13;
-import org.projectfloodlight.openflow.protocol.ver12.OFFlowModFlagsSerializerVer12;
-import org.projectfloodlight.openflow.protocol.ver12.OFPortConfigSerializerVer12;
-import org.projectfloodlight.openflow.protocol.ver12.OFPortFeaturesSerializerVer12;
-import org.projectfloodlight.openflow.protocol.ver12.OFPortStateSerializerVer12;
-import org.projectfloodlight.openflow.protocol.ver12.OFStatsReplyFlagsSerializerVer12;
-import org.projectfloodlight.openflow.protocol.ver11.OFFlowModFlagsSerializerVer11;
-import org.projectfloodlight.openflow.protocol.ver11.OFPortConfigSerializerVer11;
-import org.projectfloodlight.openflow.protocol.ver11.OFPortFeaturesSerializerVer11;
-import org.projectfloodlight.openflow.protocol.ver11.OFPortStateSerializerVer11;
-import org.projectfloodlight.openflow.protocol.ver11.OFStatsReplyFlagsSerializerVer11;
-import org.projectfloodlight.openflow.protocol.ver10.OFPortConfigSerializerVer10;
-import org.projectfloodlight.openflow.protocol.ver10.OFPortFeaturesSerializerVer10;
-import org.projectfloodlight.openflow.protocol.ver10.OFPortStateSerializerVer10;
-import org.projectfloodlight.openflow.protocol.ver10.OFStatsReplyFlagsSerializerVer10;
-import org.projectfloodlight.openflow.protocol.ver13.OFPortStateSerializerVer13;
-import org.projectfloodlight.openflow.protocol.ver13.OFPortConfigSerializerVer13;
-import org.projectfloodlight.openflow.protocol.ver14.OFFlowModFlagsSerializerVer14;
-import org.projectfloodlight.openflow.protocol.ver14.OFStatsReplyFlagsSerializerVer14;
+import org.projectfloodlight.openflow.protocol.stat.StatField;
+import org.projectfloodlight.openflow.protocol.ver15.OFMeterBandTypeSerializerVer15;
+import org.projectfloodlight.openflow.protocol.ver15.OFPortDescPropTypeSerializerVer15;
+import org.projectfloodlight.openflow.protocol.ver15.OFPortStatsPropTypeSerializerVer15;
+import org.projectfloodlight.openflow.protocol.ver15.OFTableFeaturePropTypeSerializerVer15;
 import org.projectfloodlight.openflow.protocol.OFAggregateStatsReply;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.U8;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -121,775 +140,1007 @@ import org.slf4j.LoggerFactory;
  * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
  */
 public class StatsReplySerializer extends JsonSerializer<StatsReply> {
-	protected static Logger logger = LoggerFactory.getLogger(StatsReplySerializer.class);
-	@SuppressWarnings("unchecked")
-	@Override
-	public void serialize(StatsReply reply, JsonGenerator jGen, SerializerProvider serializer) throws IOException, JsonProcessingException {
-		// Return a nice error to user if the request we're about to serialize was bad
-		if (reply.getValues() == null) {
-			jGen.writeStartObject();
-			jGen.writeObjectFieldStart("ERROR");
-			jGen.writeStringField("   ", "An error has occurred while proccesing your request,");
-			jGen.writeStringField("  *", "which might be due to one or more of the following:");
-			jGen.writeStringField(" * ", "-- An invalid DPID and/or stats/features request.");
-			jGen.writeStringField(" **", "-- The switch is not connected to the controller.");
-			jGen.writeStringField("*  ", "-- The request specified is not supported by the switch's OpenFlow version.");
-			jGen.writeEndObject();
-			jGen.writeObjectFieldStart("Valid statistics and features strings are:");
-			jGen.writeStringField("1)", OFStatsTypeStrings.AGGREGATE);
-			jGen.writeStringField("2)", OFStatsTypeStrings.DESC);
-			jGen.writeStringField("3)", OFStatsTypeStrings.EXPERIMENTER);
-			jGen.writeStringField("4)", OFStatsTypeStrings.FEATURES);
-			jGen.writeStringField("5)", OFStatsTypeStrings.FLOW);
-			jGen.writeStringField("6)", OFStatsTypeStrings.GROUP);
-			jGen.writeStringField("7)", OFStatsTypeStrings.GROUP_DESC);
-			jGen.writeStringField("8)", OFStatsTypeStrings.GROUP_FEATURES);  
-			jGen.writeStringField("9)", OFStatsTypeStrings.METER);  
-			jGen.writeStringField("A)", OFStatsTypeStrings.METER_CONFIG); 
-			jGen.writeStringField("B)", OFStatsTypeStrings.METER_FEATURES); 
-			jGen.writeStringField("C)", OFStatsTypeStrings.PORT);
-			jGen.writeStringField("D)", OFStatsTypeStrings.PORT_DESC);
-			jGen.writeStringField("E)", OFStatsTypeStrings.QUEUE);
-			jGen.writeStringField("F)", OFStatsTypeStrings.TABLE);
-			jGen.writeStringField("G)", OFStatsTypeStrings.TABLE_FEATURES);
-			jGen.writeEndObject(); 
-			jGen.writeEndObject();
-			return;
-		}
-
-		jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); // IMHO this just looks nicer and is easier to read if everything is quoted
-		jGen.writeStartObject();
-
-		if (reply.getStatType() == null) { // must be an OFFeaturesReply. getValues() was already checked for null above.
-			serializeFeaturesReply((OFFeaturesReply) reply.getValues(), jGen);
-		} else {
-			switch (reply.getStatType()) {
-			case PORT:
-				serializePortReply((List<OFPortStatsReply>) reply.getValues(), jGen);
-				break;
-			case QUEUE:
-				// handle queue
-				break;
-			case FLOW:
-				serializeFlowReply((List<OFFlowStatsReply>) reply.getValues(), jGen);
-				break;
-			case AGGREGATE:
-				serializeAggregateReply((List<OFAggregateStatsReply>) reply.getValues(), jGen);
-				break;
-			case DESC:
-				serializeDescReply((List<OFDescStatsReply>) reply.getValues(), jGen);
-				break;            
-			case GROUP:
-				serializeGroupReply((List<OFGroupStatsReply>) reply.getValues(), jGen);            
-				break;        
-			case GROUP_DESC:
-				serializeGroupDescReply((List<OFGroupDescStatsReply>) reply.getValues(), jGen);
-				break;
-			case GROUP_FEATURES:
-				serializeGroupFeaturesReply((List<OFGroupFeaturesStatsReply>) reply.getValues(), jGen);
-				break;
-			case METER:
-				serializeMeterReply((List<OFMeterStatsReply>) reply.getValues(), jGen);
-				break;
-			case METER_CONFIG:
-				serializeMeterConfigReply((List<OFMeterConfigStatsReply>) reply.getValues(), jGen);
-				break;
-			case METER_FEATURES:
-				serializeMeterFeaturesReply((List<OFMeterFeaturesStatsReply>) reply.getValues(), jGen);
-				break;            
-			case TABLE:
-				serializeTableReply((List<OFTableStatsReply>) reply.getValues(), jGen);
-				break;
-			case TABLE_FEATURES:
-				serializeTableFeaturesReply((List<OFTableFeaturesStatsReply>) reply.getValues(), jGen);
-				break;
-			case PORT_DESC:
-				serializePortDescReply((List<OFPortDescStatsReply>) reply.getValues(), jGen);
-				break;
-			case EXPERIMENTER:
-				break;
-			default:
-				break;
-			}   
-		}
-		jGen.writeEndObject();
-	}
-
-	public static void serializeFeaturesReply(OFFeaturesReply fr, JsonGenerator jGen) throws IOException, JsonProcessingException {
-		/* Common to All OF Versions */			
-		jGen.writeStringField("capabilities", fr.getCapabilities().toString());
-		jGen.writeStringField("dpid", fr.getDatapathId().toString());
-		jGen.writeNumberField("buffers", fr.getNBuffers());
-		jGen.writeNumberField("tables", fr.getNTables());
-		jGen.writeStringField("version", fr.getVersion().toString());
-
-		if (fr.getVersion().compareTo(OFVersion.OF_13) < 0) { // OF1.3+ break this out into port_config
-			serializePortDesc(fr.getPorts(), jGen);
-		}
-		if (fr.getVersion().compareTo(OFVersion.OF_10) == 0) {
-			String actions = "[";
-			for (OFActionType action : fr.getActions()) {
-				actions =  actions + action.toString() + ", ";
-			}
-			actions = actions.substring(0, actions.length() - 2); // remove ending space+comma
-			actions = actions + "]";
-			jGen.writeStringField("actions", actions);
-		}
-	}
-
-	/***
-	 * Serializes the Group Statistics Reply
-	 * @author Naveen
-	 * @param groupReplies
-	 * @param jGen
-	 * @throws IOException
-	 * @throws JsonProcessingException
-	 */
-	public static void serializeGroupReply(List<OFGroupStatsReply> groupReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-
-		OFGroupStatsReply groupReply = groupReplies.get(0); // we will get only one GroupReply and it will contains many OFGroupStatsEntry
-		jGen.writeStringField("version", groupReply.getVersion().toString()); //return the enum name
-		jGen.writeFieldName("group");
-		jGen.writeStartArray();
-		for(OFGroupStatsEntry entry : groupReply.getEntries()) {
-			jGen.writeStartObject();
-			jGen.writeStringField("groupNumber",entry.getGroup().toString());               
-			jGen.writeNumberField("refCount", entry.getRefCount());
-			jGen.writeNumberField("packetCount", entry.getPacketCount().getValue());
-			jGen.writeNumberField("byteCount", entry.getByteCount().getValue());                        
-			jGen.writeFieldName("bucketCounters");
-			jGen.writeStartArray();            
-			for(OFBucketCounter bCounter : entry.getBucketStats()) {
-				jGen.writeStartObject();
-				jGen.writeNumberField("packetCount", bCounter.getPacketCount().getValue());
-				jGen.writeNumberField("byteCount", bCounter.getByteCount().getValue());
-				jGen.writeEndObject();
-			}//end of for loop - BucketCounter
-			jGen.writeEndArray();
-			if (OFVersion.OF_13 == entry.getVersion()) {
-				jGen.writeNumberField("durationSec", entry.getDurationSec());
-				jGen.writeNumberField("durationNsec", entry.getDurationNsec());
-			}
-			jGen.writeEndObject();
-		}//end of for loop - groupStats
-		jGen.writeEndArray();
-	}
-
-	/***
-	 * Serializes Group Desc Reply
-	 * @author Naveen
-	 * @param groupDescReplies
-	 * @param jGen
-	 * @throws IOException
-	 * @throws JsonProcessingException
-	 */
-	public static void serializeGroupDescReply(List<OFGroupDescStatsReply> groupDescReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-		OFGroupDescStatsReply groupDescReply = groupDescReplies.get(0);
-		jGen.writeStringField("version", groupDescReply.getVersion().toString()); //return the enum name
-		jGen.writeFieldName("groupDesc");
-		jGen.writeStartArray();
-		for(OFGroupDescStatsEntry entry : groupDescReply.getEntries()) {
-			jGen.writeStartObject();                        
-			jGen.writeStringField("groupType",entry.getGroupType().toString());
-			jGen.writeStringField("groupNumber",entry.getGroup().toString());                                               
-			jGen.writeFieldName("buckets");            
-			jGen.writeStartArray();            
-			for(OFBucket buckets : entry.getBuckets()) {            	
-				jGen.writeStartObject();
-				jGen.writeNumberField("weight", buckets.getWeight());
-				jGen.writeNumberField("watchPortNumber", buckets.getWatchPort().getPortNumber());
-				jGen.writeStringField("watchGroup", buckets.getWatchGroup().toString());            	
-				OFActionListSerializer.serializeActions(jGen, buckets.getActions());            	
-				jGen.writeEndObject();
-			}//End of for loop - buckets
-			jGen.writeEndArray();//end of buckets            
-			jGen.writeEndObject();//end of group Desc iteration
-		}//End of for loop - GroupDescStats
-		jGen.writeEndArray();//end of group Desc
-	}
-
-	/***
-	 * Serializes Group Feature Reply 
-	 * @author Naveen
-	 * @param groupFeaturesReplies
-	 * @param jGen
-	 * @throws IOException
-	 * @throws JsonProcessingException
-	 */
-	public static void serializeGroupFeaturesReply(List<OFGroupFeaturesStatsReply> groupFeaturesReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-
-		OFGroupFeaturesStatsReply groupFeaturesReply = groupFeaturesReplies.get(0);
-		jGen.writeStringField("version", groupFeaturesReply.getVersion().toString()); //return the enum name
-
-		jGen.writeFieldName("groupFeatures");
-		jGen.writeStartObject();                        
-		jGen.writeNumberField("capabilities",groupFeaturesReply.getCapabilities());                                                              
-		jGen.writeNumberField("maxGroupsAll",groupFeaturesReply.getMaxGroupsAll());
-		jGen.writeNumberField("maxGroupsSelect",groupFeaturesReply.getMaxGroupsSelect());
-		jGen.writeNumberField("maxGroupsIndirect",groupFeaturesReply.getMaxGroupsIndirect());
-		jGen.writeNumberField("maxGroupsFf",groupFeaturesReply.getMaxGroupsFf());
-		jGen.writeNumberField("actionsAll",groupFeaturesReply.getActionsAll());
-		jGen.writeNumberField("actionsSelect",groupFeaturesReply.getActionsSelect());
-		jGen.writeNumberField("actionsIndirect",groupFeaturesReply.getActionsIndirect());
-		jGen.writeNumberField("actionsFf",groupFeaturesReply.getActionsFf());
-
-		jGen.writeEndObject();//end of group Feature
-	}
-
-	/***
-	 * Serializes the Meter Statistics Reply
-	 * @author Naveen
-	 * @param meterReplies
-	 * @param jGen
-	 * @throws IOException
-	 * @throws JsonProcessingException
-	 */
-	public static void serializeMeterReply(List<OFMeterStatsReply> meterReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-		OFMeterStatsReply meterReply = meterReplies.get(0); // we will get only one meterReply and it will contains many OFMeterStatsEntry ?
-		jGen.writeStringField("version", meterReply.getVersion().toString()); //return the enum name
-		jGen.writeFieldName("meter");
-		jGen.writeStartArray();
-		for(OFMeterStats entry : meterReply.getEntries()) {
-			jGen.writeStartObject();
-			jGen.writeNumberField("meterId",entry.getMeterId());                        
-			jGen.writeNumberField("flowCount", entry.getFlowCount());
-			jGen.writeNumberField("packetInCount", entry.getPacketInCount().getValue());
-			jGen.writeNumberField("byteInCount", entry.getByteInCount().getValue());
-			jGen.writeFieldName("meterBandStats");
-			jGen.writeStartArray();
-			for(OFMeterBandStats bandStats : entry.getBandStats()) {
-				jGen.writeStartObject();
-				jGen.writeNumberField("packetBandCount", bandStats.getPacketBandCount().getValue());
-				jGen.writeNumberField("byteBandCount", bandStats.getByteBandCount().getValue());
-				jGen.writeEndObject();
-			}//End of for loop - bandStats
-			jGen.writeEndArray();          
-
-			jGen.writeNumberField("durationSec", entry.getDurationSec());
-			jGen.writeNumberField("durationNsec", entry.getDurationNsec());            
-			jGen.writeEndObject();
-		}//End of for loop - MeterStats
-		jGen.writeEndArray();
-	}
-
-	/***
-	 * Serializes Meter Feature Reply
-	 * @author Naveen
-	 * @param meterFeaturesReplies
-	 * @param jGen
-	 * @throws IOException
-	 * @throws JsonProcessingException
-	 */
-	public static void serializeMeterFeaturesReply(List<OFMeterFeaturesStatsReply> meterFeaturesReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-		OFMeterFeaturesStatsReply meterFeaturesReply = meterFeaturesReplies.get(0);
-		jGen.writeStringField("version", meterFeaturesReply.getVersion().toString()); //return the enum name
-
-		OFMeterFeatures meterFeatures = meterFeaturesReply.getFeatures();
-		jGen.writeFieldName("meterFeatures");
-		jGen.writeStartObject();      
-
-		jGen.writeNumberField("maxGroupsAll",meterFeatures.getMaxMeter());
-		jGen.writeNumberField("maxGroupsSelect",meterFeatures.getBandTypes());
-		jGen.writeNumberField("capabilities",meterFeatures.getCapabilities());
-		jGen.writeNumberField("maxGroupsIndirect",meterFeatures.getMaxBands());
-		jGen.writeNumberField("maxGroupsFf",meterFeatures.getMaxColor());
-
-		jGen.writeEndObject();//end of group Feature
-	}
-
-	/***
-	 * Serializes Meter Config Reply
-	 * @author Naveen 
-	 * @param meterConfigReplies
-	 * @param jGen
-	 * @throws IOException
-	 * @throws JsonProcessingException
-	 */
-	public static void serializeMeterConfigReply(List<OFMeterConfigStatsReply> meterConfigReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-		OFMeterConfigStatsReply meterConfigReply = meterConfigReplies.get(0);
-		jGen.writeStringField("version", meterConfigReply.getVersion().toString()); //return the enum name
-		jGen.writeFieldName("meterConfig");
-		jGen.writeStartArray();
-		for (OFMeterConfig config : meterConfigReply.getEntries()) {
-			jGen.writeStartObject();
-			jGen.writeNumberField("meterId", config.getMeterId());
-			jGen.writeNumberField("flags", config.getFlags());
-			jGen.writeFieldName("meterBands");
-			jGen.writeStartArray();
-			for (OFMeterBand band : config.getEntries()) {
-				jGen.writeStartObject();
-				short type = (short)band.getType();
-				jGen.writeNumberField("bandType",type);
-
-				switch (type) {
-				case OFMeterBandTypeSerializerVer13.DROP_VAL:
-					OFMeterBandDrop bandDrop = (OFMeterBandDrop) band;
-					jGen.writeNumberField("rate", bandDrop.getRate());
-					jGen.writeNumberField("burstSize", bandDrop.getBurstSize());
-					break;
-
-				case OFMeterBandTypeSerializerVer13.DSCP_REMARK_VAL:
-					OFMeterBandDscpRemark bandDscp = (OFMeterBandDscpRemark) band;
-					jGen.writeNumberField("rate", bandDscp.getRate());
-					jGen.writeNumberField("burstSize", bandDscp.getBurstSize());
-					jGen.writeNumberField("precLevel", bandDscp.getPrecLevel());
-					break;
-
-				case OFMeterBandTypeSerializerVer13.EXPERIMENTER_VAL:
-					OFMeterBandExperimenter bandExp = (OFMeterBandExperimenter) band;
-					jGen.writeNumberField("rate", bandExp.getRate());
-					jGen.writeNumberField("burstSize", bandExp.getBurstSize());
-					jGen.writeNumberField("experimenter", bandExp.getExperimenter());
-					break;
-
-				default:
-					// shouldn't ever get here
-					break;            		
-				}//end of Switch Case
-
-				jGen.writeEndObject();
-			}//end of for loop
-			jGen.writeEndArray();
-			jGen.writeEndObject();
-		}//end of for loop
-		jGen.writeEndArray();
-	}
-
-	/***
-	 * Serializes Table Statistics
-	 * @author Naveen
-	 * @param tableReplies
-	 * @param jGen
-	 * @throws IOException
-	 * @throws JsonProcessingException
-	 */
-	public static void serializeTableReply(List<OFTableStatsReply> tableReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-
-		OFTableStatsReply tableReply = tableReplies.get(0); // we will get only one tableReply and it will contains many OFTableStatsEntry ?
-		jGen.writeStringField("version", tableReply.getVersion().toString()); //return the enum name
-		jGen.writeFieldName("table");
-		jGen.writeStartArray();
-		for(OFTableStatsEntry entry : tableReply.getEntries()) {
-			jGen.writeStartObject();
-
-			//Fields common to all OF versions
-			//For OF 1.3, only these fields are applicable
-			jGen.writeStringField("tableId",entry.getTableId().toString());                        
-			jGen.writeNumberField("activeCount", entry.getActiveCount());
-			jGen.writeNumberField("lookUpCount", entry.getLookupCount().getValue());
-			jGen.writeNumberField("matchCount", entry.getMatchedCount().getValue());
-
-			//Fields Applicable only for specific Versions
-			switch (entry.getVersion()) {            
-			case OF_12:
-				//Fields applicable only to OF 1.2
-				jGen.writeNumberField("writeSetFields", entry.getWriteSetfields().getValue());
-				jGen.writeNumberField("applySetFields", entry.getApplySetfields().getValue());
-				jGen.writeNumberField("metaDataMatch", entry.getMetadataMatch().getValue());
-				jGen.writeNumberField("metaDataWrite", entry.getMetadataWrite().getValue());            
-			case OF_11:
-				//Fields applicable to OF 1.1 & 1.2
-				jGen.writeStringField("match", entry.getMatch().toString());
-				jGen.writeNumberField("instructions", entry.getInstructions());
-				jGen.writeNumberField("writeActions", entry.getWriteActions());
-				jGen.writeNumberField("applyActions", entry.getApplyActions());
-				jGen.writeNumberField("config", entry.getConfig());            	
-			case OF_10:
-				//Fields applicable to OF 1.0, 1.1 & 1.2 
-				jGen.writeStringField("name",entry.getName());                        
-				jGen.writeNumberField("wildcards", entry.getWildcards());
-				jGen.writeNumberField("maxEntries", entry.getMaxEntries());
-				break;                   
-			default:
-				//no extra fields for OF_13
-				break;            	
-			}//End of switch case
-			jGen.writeEndObject();
-		}//End of for loop
-		jGen.writeEndArray();
-	}
-
-	/***
-	 * Serializes Table Features Reply
-	 * @author Naveen
-	 * @param tableFeaturesReplies
-	 * @param jGen
-	 * @throws IOException
-	 * @throws JsonProcessingException
-	 */
-	public static void serializeTableFeaturesReply(List<OFTableFeaturesStatsReply> tableFeaturesReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-
-		jGen.writeFieldName("tableFeatures");
-		jGen.writeStartArray();
-		for (OFTableFeaturesStatsReply tableFeaturesReply : tableFeaturesReplies) {
-
-			for(OFTableFeatures tableFeature : tableFeaturesReply.getEntries()) {
-				jGen.writeStartObject();    
-				jGen.writeStringField("version", tableFeature.getVersion().toString());
-				jGen.writeNumberField("tableId", tableFeature.getTableId().getValue());
-				jGen.writeStringField("name", tableFeature.getName());
-				jGen.writeNumberField("metadataMatch", tableFeature.getMetadataMatch().getValue());
-				jGen.writeNumberField("metadataWrite", tableFeature.getMetadataWrite().getValue());
-				jGen.writeNumberField("config", tableFeature.getConfig());
-				jGen.writeNumberField("maxEntries", tableFeature.getMaxEntries());
-
-				jGen.writeFieldName("properties");
-				jGen.writeStartArray();
-				for (OFTableFeatureProp properties : tableFeature.getProperties()) {            	
-					jGen.writeStartObject();
-
-					short type = (short)properties.getType();
-					switch (type) {
-					case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_VAL:
-						OFTableFeaturePropInstructions propInstruct = (OFTableFeaturePropInstructions) properties;
-						jGen.writeFieldName("instructions");
-						jGen.writeStartArray();
-						for (OFInstructionId id : propInstruct.getInstructionIds()) {
-							jGen.writeString(id.getType().toString());              			
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_MISS_VAL:
-						OFTableFeaturePropInstructionsMiss propInstructMiss = (OFTableFeaturePropInstructionsMiss) properties;
-						jGen.writeFieldName("instructionsMiss");
-						jGen.writeStartArray();
-						for (OFInstructionId id : propInstructMiss.getInstructionIds()) {
-							jGen.writeString(id.getType().toString());              			
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_VAL:
-						OFTableFeaturePropNextTables propNxtTables = (OFTableFeaturePropNextTables) properties;
-						jGen.writeFieldName("nextTables");
-						jGen.writeStartArray();
-						for (U8 id : propNxtTables.getNextTableIds()) {
-							jGen.writeNumber(id.getValue());
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_MISS_VAL:
-						OFTableFeaturePropNextTablesMiss propNxtTablesMiss = (OFTableFeaturePropNextTablesMiss) properties;
-						jGen.writeFieldName("nextTablesMiss");
-						jGen.writeStartArray();
-						for (U8 id : propNxtTablesMiss.getNextTableIds()) {
-							jGen.writeNumber(id.getValue());
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_VAL:
-						OFTableFeaturePropWriteActions propWrAct = (OFTableFeaturePropWriteActions) properties; 
-						jGen.writeFieldName("writeActions");
-						jGen.writeStartArray();
-						for (OFActionId id : propWrAct.getActionIds()) {
-							jGen.writeString(id.getType().toString());
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_MISS_VAL:
-						OFTableFeaturePropWriteActionsMiss propWrActMiss = (OFTableFeaturePropWriteActionsMiss) properties;
-						jGen.writeFieldName("writeActionsMiss");
-						jGen.writeStartArray();
-						for (OFActionId id : propWrActMiss.getActionIds()) {
-							jGen.writeString(id.getType().toString());
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_VAL:
-						OFTableFeaturePropApplyActions propAppAct = (OFTableFeaturePropApplyActions) properties;   
-						jGen.writeFieldName("applyActions");
-						jGen.writeStartArray();
-						for (OFActionId id : propAppAct.getActionIds()) {
-							jGen.writeString(id.getType().toString());
-						}
-						jGen.writeEndArray();
-						break;	
-					case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_MISS_VAL:
-						OFTableFeaturePropApplyActionsMiss propAppActMiss = (OFTableFeaturePropApplyActionsMiss) properties;
-						jGen.writeFieldName("applyActionsMiss");
-						jGen.writeStartArray();
-						for (OFActionId id : propAppActMiss.getActionIds()) {
-							jGen.writeString(id.getType().toString());
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.MATCH_VAL:                	
-						OFTableFeaturePropMatch propMatch = (OFTableFeaturePropMatch) properties;
-						jGen.writeFieldName("match");
-						jGen.writeStartArray();
-						for (U32 id : propMatch.getOxmIds()) {
-							jGen.writeString(OXMSerializer.oxmIdToString(id));
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.WILDCARDS_VAL:
-						OFTableFeaturePropWildcards propWildcards = (OFTableFeaturePropWildcards) properties;
-						jGen.writeFieldName("wildcards");
-						jGen.writeStartArray();
-						for (U32 id : propWildcards.getOxmIds()) {
-							jGen.writeString(OXMSerializer.oxmIdToString(id));
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_VAL:
-						OFTableFeaturePropWriteSetfield propWrSetfield = (OFTableFeaturePropWriteSetfield) properties;           
-						jGen.writeFieldName("writeSetfield");
-						jGen.writeStartArray();
-						for (U32 id : propWrSetfield.getOxmIds()) {
-							jGen.writeString(OXMSerializer.oxmIdToString(id));
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_MISS_VAL:
-						OFTableFeaturePropWriteSetfieldMiss propWrSetfieldMiss = (OFTableFeaturePropWriteSetfieldMiss) properties; 
-						jGen.writeFieldName("writeSetfieldMiss");
-						jGen.writeStartArray();
-						for (U32 id : propWrSetfieldMiss.getOxmIds()) {
-							jGen.writeString(OXMSerializer.oxmIdToString(id));
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_VAL:
-						OFTableFeaturePropApplySetfield propAppSetfield = (OFTableFeaturePropApplySetfield) properties;
-						jGen.writeFieldName("applySetfield");
-						jGen.writeStartArray();
-						for (U32 id : propAppSetfield.getOxmIds()) {
-							jGen.writeString(OXMSerializer.oxmIdToString(id));
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_MISS_VAL:
-						OFTableFeaturePropApplySetfieldMiss propAppSetfieldMiss = (OFTableFeaturePropApplySetfieldMiss) properties;                		
-						jGen.writeFieldName("applySetfieldMiss");
-						jGen.writeStartArray();
-						for (U32 id : propAppSetfieldMiss.getOxmIds()) {
-							jGen.writeString(OXMSerializer.oxmIdToString(id));
-						}
-						jGen.writeEndArray();
-						break;
-					case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_VAL:
-						OFTableFeaturePropExperimenter propExp = (OFTableFeaturePropExperimenter) properties; 
-						jGen.writeFieldName("experimenter");
-						jGen.writeStartObject();
-						jGen.writeNumberField("subType", propExp.getSubtype());
-						jGen.writeNumberField("experimenter", propExp.getExperimenter());
-						jGen.writeStringField("data", propExp.getExperimenterData().toString());
-						jGen.writeEndObject();
-						break;	
-					case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_MISS_VAL:
-						OFTableFeaturePropExperimenterMiss propExpMiss = (OFTableFeaturePropExperimenterMiss) properties;
-						jGen.writeFieldName("experimenterMiss");
-						jGen.writeStartObject();
-						jGen.writeNumberField("subType", propExpMiss.getSubtype());
-						jGen.writeNumberField("experimenter", propExpMiss.getExperimenter());
-						jGen.writeStringField("data", propExpMiss.getExperimenterData().toString());
-						jGen.writeEndObject();
-						break;	
-					default:
-						// shouldn't ever get here
-						jGen.writeStartObject();
-						jGen.writeEndObject();
-						break;            		
-					}//end of Switch Case  
-					jGen.writeEndObject();
-				}//end of for loop - properties                                              
-				jGen.writeEndArray();
-				jGen.writeEndObject();
-			}//end of for loop - features
-		} //end of looping through REQ_MORE flagged message loop
-		jGen.writeEndArray();
-	} 
-
-	public static void serializePortReply(List<OFPortStatsReply> portReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{		
-		jGen.writeFieldName("port_reply");
-		jGen.writeStartArray();
-		for (OFPortStatsReply portReply : portReplies) {
-			jGen.writeStartObject();
-			jGen.writeStringField("version", portReply.getVersion().toString()); //return the enum name
-			jGen.writeFieldName("port");
-			jGen.writeStartArray();
-			for(OFPortStatsEntry entry : portReply.getEntries()) {
-				jGen.writeStartObject();
-				jGen.writeStringField("portNumber",entry.getPortNo().toString());
-				jGen.writeNumberField("receivePackets", entry.getRxPackets().getValue());
-				jGen.writeNumberField("transmitPackets", entry.getTxPackets().getValue());
-				jGen.writeNumberField("receiveBytes", entry.getRxBytes().getValue());
-				jGen.writeNumberField("transmitBytes", entry.getTxBytes().getValue());
-				jGen.writeNumberField("receiveDropped", entry.getRxDropped().getValue());
-				jGen.writeNumberField("transmitDropped", entry.getTxDropped().getValue());
-				jGen.writeNumberField("receiveErrors", entry.getRxErrors().getValue());
-				jGen.writeNumberField("transmitErrors", entry.getTxErrors().getValue());
-				jGen.writeNumberField("receiveFrameErrors", entry.getRxFrameErr().getValue());
-				jGen.writeNumberField("receiveOverrunErrors", entry.getRxOverErr().getValue());
-				jGen.writeNumberField("receiveCRCErrors", entry.getRxCrcErr().getValue());
-				jGen.writeNumberField("collisions", entry.getCollisions().getValue());
-				if (OFVersion.OF_13 == entry.getVersion()) {
-					jGen.writeNumberField("durationSec", entry.getDurationSec());
-					jGen.writeNumberField("durationNsec", entry.getDurationNsec());
-				}
-				jGen.writeEndObject();
-			}
-			jGen.writeEndArray();
-			jGen.writeEndObject();
-		}
-		jGen.writeEndArray();
-	}
-
-	public static void serializeFlowReply(List<OFFlowStatsReply> flowReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-		/* start the array before each reply */
-		jGen.writeFieldName("flows"); 
-		jGen.writeStartArray();
-		for (OFFlowStatsReply flowReply : flowReplies) { // for each flow stats reply
-			List<OFFlowStatsEntry> entries = flowReply.getEntries();
-			for (OFFlowStatsEntry entry : entries) { // for each flow
-				jGen.writeStartObject();
-				// list flow stats/info
-				jGen.writeStringField("version", entry.getVersion().toString()); // return the enum name
-				jGen.writeNumberField("cookie", entry.getCookie().getValue());
-				jGen.writeStringField("tableId", entry.getTableId().toString());
-				jGen.writeNumberField("packetCount", entry.getPacketCount().getValue());
-				jGen.writeNumberField("byteCount", entry.getByteCount().getValue());
-				jGen.writeNumberField("durationSeconds", entry.getDurationSec());
-				jGen.writeNumberField("durationNSeconds", entry.getDurationNsec());
-				jGen.writeNumberField("priority", entry.getPriority());
-				jGen.writeNumberField("idleTimeoutSec", entry.getIdleTimeout());
-				jGen.writeNumberField("hardTimeoutSec", entry.getHardTimeout());
-				switch (entry.getVersion()) {
-				case OF_10:
-					// flags not supported
-					break;
-				case OF_11:
-					jGen.writeNumberField("flags", OFFlowModFlagsSerializerVer11.toWireValue(entry.getFlags()));
-					break;
-				case OF_12:
-					jGen.writeNumberField("flags", OFFlowModFlagsSerializerVer12.toWireValue(entry.getFlags()));
-					break;
-				case OF_13:
-					jGen.writeNumberField("flags", OFFlowModFlagsSerializerVer13.toWireValue(entry.getFlags()));
-					break;
-				case OF_14:
-					jGen.writeNumberField("flags", OFFlowModFlagsSerializerVer14.toWireValue(entry.getFlags()));
-					break;
-				default:
-					logger.error("Could not decode OFVersion {}", entry.getVersion());
-					break;
-				}
-
-				MatchSerializer.serializeMatch(jGen, entry.getMatch());
-
-				// handle OF1.1+ instructions with actions within
-				if (entry.getVersion() == OFVersion.OF_10) {
-					jGen.writeObjectFieldStart("actions");
-					OFActionListSerializer.serializeActions(jGen, entry.getActions());
-					jGen.writeEndObject();
-				} else {
-					OFInstructionListSerializer.serializeInstructionList(jGen, entry.getInstructions());
-				}
-
-				jGen.writeEndObject();
-			} // end for each OFFlowStatsReply entry */
-		} // end for each OFStatsReply
-		//jGen.writeEndObject();
-		jGen.writeEndArray();
-	} // end method
-
-	public static void serializeDescReply(List<OFDescStatsReply> descReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-		OFDescStatsReply descReply = descReplies.get(0); // There is only one descReply from the switch
-		jGen.writeObjectFieldStart("desc"); 
-		jGen.writeStringField("version", descReply.getVersion().toString()); //return the enum name
-		jGen.writeStringField("manufacturerDescription", descReply.getMfrDesc()); 
-		jGen.writeStringField("hardwareDescription", descReply.getHwDesc()); 
-		jGen.writeStringField("softwareDescription", descReply.getSwDesc()); 
-		jGen.writeStringField("serialNumber", descReply.getSerialNum()); 
-		jGen.writeStringField("datapathDescription", descReply.getDpDesc()); 
-		jGen.writeEndObject(); // end match
-	}
-
-	public static void serializeAggregateReply(List<OFAggregateStatsReply> aggregateReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-		OFAggregateStatsReply aggregateReply = aggregateReplies.get(0); // There are only one aggregateReply from the switch
-		jGen.writeObjectFieldStart("aggregate"); 
-		jGen.writeStringField("version", aggregateReply.getVersion().toString()); //return the enum name
-		jGen.writeNumberField("flowCount", aggregateReply.getFlowCount());
-		jGen.writeNumberField("packetCount", aggregateReply.getPacketCount().getValue());
-		jGen.writeNumberField("byteCount", aggregateReply.getByteCount().getValue());
-		switch (aggregateReply.getVersion()) {
-		case OF_10:
-			jGen.writeNumberField("flags", OFStatsReplyFlagsSerializerVer10.toWireValue(aggregateReply.getFlags()));
-			break;
-		case OF_11:
-			jGen.writeNumberField("flags", OFStatsReplyFlagsSerializerVer11.toWireValue(aggregateReply.getFlags()));
-			break;
-		case OF_12:
-			jGen.writeNumberField("flags", OFStatsReplyFlagsSerializerVer12.toWireValue(aggregateReply.getFlags()));
-			break;
-		case OF_13:
-			jGen.writeNumberField("flags", OFStatsReplyFlagsSerializerVer13.toWireValue(aggregateReply.getFlags()));
-			break;
-		case OF_14:
-			jGen.writeNumberField("flags", OFStatsReplyFlagsSerializerVer14.toWireValue(aggregateReply.getFlags()));
-			break;
-		default:
-			break;
-		}
-		jGen.writeEndObject(); // end match
-	}
-
-	public static void serializePortDescReply(List<OFPortDescStatsReply> portDescReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
-		OFPortDescStatsReply portDescReply = portDescReplies.get(0); // we will get only one PortDescReply and it will contains many OFPortDescStatsEntry ?
-		jGen.writeStringField("version", portDescReply.getVersion().toString()); //return the enum name
-		serializePortDesc(portDescReply.getEntries(), jGen);
-	}
-
-	public static void serializePortDesc(List<OFPortDesc> portDescList, JsonGenerator jGen) throws IOException, JsonProcessingException {
-		jGen.writeFieldName("portDesc");
-		jGen.writeStartArray();
-		for(OFPortDesc entry : portDescList) {
-			jGen.writeStartObject();
-			jGen.writeStringField("portNumber",entry.getPortNo().toString());
-			jGen.writeStringField("hardwareAddress", entry.getHwAddr().toString());
-			jGen.writeStringField("name", entry.getName());
-			switch(entry.getVersion()) {
-			case OF_10:
-				jGen.writeNumberField("config", OFPortConfigSerializerVer10.toWireValue(entry.getConfig()));
-				jGen.writeNumberField("state", OFPortStateSerializerVer10.toWireValue(entry.getState()));
-				jGen.writeNumberField("currentFeatures", OFPortFeaturesSerializerVer10.toWireValue(entry.getCurr()));
-				jGen.writeNumberField("advertisedFeatures", OFPortFeaturesSerializerVer10.toWireValue(entry.getAdvertised()));
-				jGen.writeNumberField("supportedFeatures", OFPortFeaturesSerializerVer10.toWireValue(entry.getSupported()));
-				jGen.writeNumberField("peerFeatures", OFPortFeaturesSerializerVer10.toWireValue(entry.getPeer()));
-				break;
-			case OF_11:
-				jGen.writeNumberField("config", OFPortConfigSerializerVer11.toWireValue(entry.getConfig()));
-				jGen.writeNumberField("state", OFPortStateSerializerVer11.toWireValue(entry.getState()));
-				jGen.writeNumberField("currentFeatures", OFPortFeaturesSerializerVer11.toWireValue(entry.getCurr()));
-				jGen.writeNumberField("advertisedFeatures", OFPortFeaturesSerializerVer11.toWireValue(entry.getAdvertised()));
-				jGen.writeNumberField("supportedFeatures", OFPortFeaturesSerializerVer11.toWireValue(entry.getSupported()));
-				jGen.writeNumberField("peerFeatures", OFPortFeaturesSerializerVer11.toWireValue(entry.getPeer()));
-				break;
-			case OF_12:
-				jGen.writeNumberField("config", OFPortConfigSerializerVer12.toWireValue(entry.getConfig()));
-				jGen.writeNumberField("state", OFPortStateSerializerVer12.toWireValue(entry.getState()));
-				jGen.writeNumberField("currentFeatures", OFPortFeaturesSerializerVer12.toWireValue(entry.getCurr()));
-				jGen.writeNumberField("advertisedFeatures", OFPortFeaturesSerializerVer12.toWireValue(entry.getAdvertised()));
-				jGen.writeNumberField("supportedFeatures", OFPortFeaturesSerializerVer12.toWireValue(entry.getSupported()));
-				jGen.writeNumberField("peerFeatures", OFPortFeaturesSerializerVer12.toWireValue(entry.getPeer()));
-				break;
-			case OF_13:
-				jGen.writeNumberField("config", OFPortConfigSerializerVer13.toWireValue(entry.getConfig()));
-				jGen.writeNumberField("state", OFPortStateSerializerVer13.toWireValue(entry.getState()));
-				jGen.writeNumberField("currentFeatures", OFPortFeaturesSerializerVer13.toWireValue(entry.getCurr()));
-				jGen.writeNumberField("advertisedFeatures", OFPortFeaturesSerializerVer13.toWireValue(entry.getAdvertised()));
-				jGen.writeNumberField("supportedFeatures", OFPortFeaturesSerializerVer13.toWireValue(entry.getSupported()));
-				jGen.writeNumberField("peerFeatures", OFPortFeaturesSerializerVer13.toWireValue(entry.getPeer()));
-				break;
-			case OF_14:
-				// TODO
-				logger.error("OF1.4 OFPortDesc serializer not implemented");
-			}
-			if (OFVersion.OF_10 != entry.getVersion()) {
-				jGen.writeNumberField("currSpeed",entry.getCurrSpeed());
-				jGen.writeNumberField("maxSpeed",entry.getMaxSpeed());
-			}
-			jGen.writeEndObject();
-		}
-		jGen.writeEndArray();
-	}
-} 
+    protected static Logger logger = LoggerFactory.getLogger(StatsReplySerializer.class);
+    @SuppressWarnings("unchecked")
+    @Override
+    public void serialize(StatsReply reply, JsonGenerator jGen, SerializerProvider serializer) throws IOException, JsonProcessingException {
+        // Return a nice error to user if the request we're about to serialize was bad
+        if (reply.getValues() == null) {
+            jGen.writeStartObject();
+            jGen.writeStringField("   ", "An error has occurred while proccesing your request,");
+            jGen.writeStringField("  *", "which might be due to one or more of the following:");
+            jGen.writeStringField(" * ", "-- An invalid DPID and/or stats/features request.");
+            jGen.writeStringField(" **", "-- The switch is not connected to the controller.");
+            jGen.writeStringField("*  ", "-- The request specified is not supported by the switch's OpenFlow version.");
+            jGen.writeArrayFieldStart("Valid statistics and features are");
+            for (Field f : OFStatsTypeStrings.class.getFields()) {
+                try {
+                    jGen.writeString((String) f.get(null)); /* expect all static String types */
+                } catch (IllegalAccessException | IllegalArgumentException e) {
+                    logger.warn("Caught unexpected, possible non-string field in OFStatsTypeStrings class");
+                }
+            }
+            jGen.writeEndArray();
+            jGen.writeEndObject(); 
+            return;
+        }
+
+        jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); // IMHO this just looks nicer and is easier to read if everything is quoted
+        jGen.writeStartObject();
+
+        if (reply.getStatType() == null) { // must be an OFFeaturesReply. getValues() was already checked for null above.
+            serializeFeaturesReply((OFFeaturesReply) reply.getValues(), jGen);
+        } else {
+            switch (reply.getStatType()) {
+            case PORT:
+                serializePortReply((List<OFPortStatsReply>) reply.getValues(), jGen);
+                break;
+            case PORT_DESC:
+                serializePortDescReply((List<OFPortDescStatsReply>) reply.getValues(), jGen);
+                break;
+            case QUEUE:
+                serializeQueueReply((List<OFQueueStatsReply>) reply.getValues(), jGen);
+                break;
+            case QUEUE_DESC:
+                serializeQueueDescReply((List<OFQueueDescStatsReply>) reply.getValues(), jGen);
+                break;
+            case FLOW:
+                serializeFlowReply((List<OFFlowStatsReply>) reply.getValues(), jGen);
+                break;
+            case FLOW_LIGHTWEIGHT:
+                serializeFlowLightweightReply((List<OFFlowLightweightStatsReply>) reply.getValues(), jGen);
+                break;
+            case FLOW_MONITOR:
+                serializeFlowMonitorReply((List<OFFlowMonitorReply>) reply.getValues(), jGen);
+                break;
+            case AGGREGATE:
+                serializeAggregateReply((List<OFAggregateStatsReply>) reply.getValues(), jGen);
+                break;
+            case DESC:
+                serializeDescReply((List<OFDescStatsReply>) reply.getValues(), jGen);
+                break;            
+            case GROUP:
+                serializeGroupReply((List<OFGroupStatsReply>) reply.getValues(), jGen);            
+                break;        
+            case GROUP_DESC:
+                serializeGroupDescReply((List<OFGroupDescStatsReply>) reply.getValues(), jGen);
+                break;
+            case GROUP_FEATURES:
+                serializeGroupFeaturesReply((List<OFGroupFeaturesStatsReply>) reply.getValues(), jGen);
+                break;
+            case METER:
+                serializeMeterReply((List<OFMeterStatsReply>) reply.getValues(), jGen);
+                break;
+            case METER_CONFIG:
+                serializeMeterConfigReply((List<OFMeterConfigStatsReply>) reply.getValues(), jGen);
+                break;
+            case METER_FEATURES:
+                serializeMeterFeaturesReply((List<OFMeterFeaturesStatsReply>) reply.getValues(), jGen);
+                break;     
+            case TABLE:
+                serializeTableReply((List<OFTableStatsReply>) reply.getValues(), jGen);
+                break;
+            case TABLE_FEATURES:
+                serializeTableFeaturesReply((List<OFTableFeaturesStatsReply>) reply.getValues(), jGen);
+                break;
+            case TABLE_DESC:
+                serializeTableDescReply((List<OFTableDescStatsReply>) reply.getValues(), jGen);
+                break;
+            case BUNDLE_FEATURES:
+                serializeBundleFeaturesReply((List<OFBundleFeaturesStatsReply>) reply.getValues(), jGen);
+                break;
+            case CONTROLLER_STATUS:
+                serializeControllerStatusReply((List<OFControllerStatusStatsReply>) reply.getValues(), jGen);
+                break;
+            case EXPERIMENTER:
+                logger.warn("Unimplemented {} stats reply serializer", reply.getStatType());
+                break;
+                /* omit default to alert (via warning) if we miss any in the future */
+            }   
+        }
+        jGen.writeEndObject();
+    }
+
+    public static void serializeFeaturesReply(OFFeaturesReply fr, JsonGenerator jGen) throws IOException, JsonProcessingException {
+        /* Common to All OF Versions */			
+        jGen.writeStringField("capabilities", fr.getCapabilities().toString());
+        jGen.writeStringField("dpid", fr.getDatapathId().toString());
+        jGen.writeNumberField("buffers", fr.getNBuffers());
+        jGen.writeNumberField("tables", fr.getNTables());
+        jGen.writeStringField("version", fr.getVersion().toString());
+
+        if (fr.getVersion().compareTo(OFVersion.OF_13) < 0) { // OF1.3+ break this out into port_config
+            serializePortDesc(fr.getPorts(), jGen);
+        }
+        if (fr.getVersion().compareTo(OFVersion.OF_10) == 0) {
+            String actions = "[";
+            for (OFActionType action : fr.getActions()) {
+                actions =  actions + action.toString() + ", ";
+            }
+            actions = actions.substring(0, actions.length() - 2); // remove ending space+comma
+            actions = actions + "]";
+            jGen.writeStringField("actions", actions);
+        }
+    }
+
+    public static void serializeTableDescReply(List<OFTableDescStatsReply> trl, JsonGenerator jGen) throws IOException, JsonProcessingException {		
+        Set<OFTableDesc> entries = new HashSet<OFTableDesc>();
+        for (OFTableDescStatsReply r : trl) {
+            entries.addAll(r.getEntries());
+        }
+        if (!entries.isEmpty()) {
+            jGen.writeStringField("version", entries.iterator().next().getVersion().toString()); /* common to all */
+            jGen.writeFieldName("tables");
+            jGen.writeStartArray();
+            for (OFTableDesc e : entries) {
+                jGen.writeNumberField("table_id", e.getTableId().getValue());
+                jGen.writeFieldName("config");
+                jGen.writeStartArray();
+                for (OFTableConfig c : e.getConfig()) {
+                    jGen.writeString(c.name());
+                }
+                jGen.writeEndArray();
+                /* TODO properties */
+            }
+            jGen.writeEndArray();
+        }
+    }
+
+    public static void serializeBundleFeaturesReply(List<OFBundleFeaturesStatsReply> bsr, JsonGenerator jGen) throws IOException, JsonProcessingException {		
+        if (!bsr.isEmpty()) {
+            OFBundleFeaturesStatsReply b =  bsr.iterator().next();
+            jGen.writeStringField("version", b.getVersion().toString());
+            jGen.writeFieldName("capabilities");
+            jGen.writeStartArray();
+            for (OFBundleFeatureFlags f : b.getCapabilities()) {
+                jGen.writeString(f.name());
+            }
+            jGen.writeEndArray();
+            /* TODO properties */
+        }
+    }
+
+    public static void serializeControllerStatusReply(List<OFControllerStatusStatsReply> csr, JsonGenerator jGen) throws IOException, JsonProcessingException {		
+        Set<OFControllerStatusEntry> entries = new HashSet<OFControllerStatusEntry>();
+        for (OFControllerStatusStatsReply r : csr) {
+            entries.addAll(r.getControllerStatus());
+        }
+        if (!entries.isEmpty()) {
+            jGen.writeStringField("version", entries.iterator().next().getVersion().toString()); /* common to all */
+            jGen.writeFieldName("controller_status");
+            jGen.writeStartArray();
+            for (OFControllerStatusEntry e : entries) {
+                jGen.writeStartObject();
+                jGen.writeNumberField("controller_id", e.getShortId());
+                jGen.writeStringField("channel_status", e.getChannelStatus().name());
+                jGen.writeStringField("reason", e.getReason().name());
+                jGen.writeStringField("role", e.getRole().name());
+                /* TODO properties */
+                jGen.writeEndObject();
+            }
+            jGen.writeEndArray();
+        }
+    }
+
+    public static void serializeQueueReply(List<OFQueueStatsReply> qrl, JsonGenerator jGen) throws IOException, JsonProcessingException {		
+        Set<OFQueueStatsEntry> entries = new HashSet<OFQueueStatsEntry>();
+        for (OFQueueStatsReply r : qrl) {
+            entries.addAll(r.getEntries());
+        }
+        if (!entries.isEmpty()) {
+            jGen.writeStringField("version", entries.iterator().next().getVersion().toString()); /* common to all */
+            jGen.writeFieldName("queues");
+            jGen.writeStartArray();
+            for (OFQueueStatsEntry e : entries) {
+                jGen.writeNumberField("duration_nsec", e.getDurationNsec());
+                jGen.writeNumberField("duration_sec", e.getDurationSec());
+                jGen.writeNumberField("queue_id", e.getQueueId());
+                jGen.writeNumberField("port", e.getPortNo().getPortNumber());
+                jGen.writeNumberField("tx_bytes", e.getTxBytes().getValue());
+                jGen.writeNumberField("tx_errors", e.getTxErrors().getValue());
+                jGen.writeNumberField("tx_errors", e.getTxPackets().getValue());
+                /* TODO properties */
+            }
+            jGen.writeEndArray();
+        }
+    }
+
+    public static void serializeQueueDescReply(List<OFQueueDescStatsReply> qrl, JsonGenerator jGen) throws IOException, JsonProcessingException {		
+        Set<OFQueueDesc> entries = new HashSet<OFQueueDesc>();
+        for (OFQueueDescStatsReply r : qrl) {
+            entries.addAll(r.getEntries());
+        }
+        if (!entries.isEmpty()) {
+            jGen.writeStringField("version", entries.iterator().next().getVersion().toString()); /* common to all */
+            jGen.writeFieldName("queues");
+            jGen.writeStartArray();
+            for (OFQueueDesc e : entries) {
+                jGen.writeNumberField("queue_id", e.getQueueId());
+                jGen.writeNumberField("port", e.getPortNo());
+                /* TODO properties */
+            }
+            jGen.writeEndArray();
+        }
+    }
+
+    public static void serializeFlowLightweightReply(List<OFFlowLightweightStatsReply> frl, JsonGenerator jGen) throws IOException, JsonProcessingException {		
+        Set<OFFlowLightweightStatsEntry> entries = new HashSet<OFFlowLightweightStatsEntry>();
+        for (OFFlowLightweightStatsReply r : frl) {
+            entries.addAll(r.getEntries());
+        }
+        if (!entries.isEmpty()) {
+            jGen.writeStringField("version", entries.iterator().next().getVersion().toString()); /* common to all */
+            jGen.writeFieldName("flows");
+            jGen.writeStartArray();
+            for (OFFlowLightweightStatsEntry e : entries) {
+                jGen.writeStartObject();
+                jGen.writeNumberField("priority", e.getPriority());
+                jGen.writeNumberField("table_id", e.getTableId().getValue());
+                MatchSerializer.serializeMatch(jGen, e.getMatch());
+                jGen.writeFieldName("stats");
+                jGen.writeStartObject();
+                for (StatField<?> f : e.getStats().getStatFields()) {
+                    switch (f.id) {
+                    case BYTE_COUNT:
+                    case IDLE_TIME:
+                    case PACKET_COUNT:
+                    case DURATION:
+                        jGen.writeNumberField(f.getName(), ((U64) e.getStats().get(f)).getValue());
+                        break;
+                    case FLOW_COUNT:
+                        jGen.writeNumberField(f.getName(), ((U32) e.getStats().get(f)).getValue());
+                        break;
+                        /* no default so we see a warning in future */
+                    }
+                }
+                jGen.writeEndObject(); /* end stats */
+                jGen.writeEndObject(); /* end entry */
+            }
+            jGen.writeEndArray();
+        }
+    }
+
+    public static void serializeFlowMonitorReply(List<OFFlowMonitorReply> fmr, JsonGenerator jGen) throws IOException, JsonProcessingException {		
+        Set<OFFlowMonitorReplyEntry> entries = new HashSet<OFFlowMonitorReplyEntry>(); 
+        for (OFFlowMonitorReply r : fmr) { 
+            entries.addAll(r.getEntries());
+        }
+        if (!entries.isEmpty()) {
+            jGen.writeStringField("version", entries.iterator().next().getVersion().toString()); /* common to all */
+            jGen.writeFieldName("events");
+            jGen.writeStartArray();
+            for (OFFlowMonitorReplyEntry e : entries) {
+                jGen.writeString(e.getEvent().name());
+            }
+            jGen.writeEndArray();
+        }
+    }
+
+    /***
+     * Serializes the Group Statistics Reply
+     * @author Naveen
+     * @param groupReplies
+     * @param jGen
+     * @throws IOException
+     * @throws JsonProcessingException
+     */
+    public static void serializeGroupReply(List<OFGroupStatsReply> groupReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+        OFGroupStatsReply groupReply = groupReplies.get(0); // we will get only one GroupReply and it will contains many OFGroupStatsEntry
+        jGen.writeStringField("version", groupReply.getVersion().toString()); //return the enum name
+        jGen.writeFieldName("group");
+        jGen.writeStartArray();
+        for (OFGroupStatsEntry entry : groupReply.getEntries()) {
+            jGen.writeStartObject();
+            jGen.writeStringField("group_number",entry.getGroup().toString());               
+            jGen.writeNumberField("reference_count", entry.getRefCount());
+            jGen.writeNumberField("packet_count", entry.getPacketCount().getValue());
+            jGen.writeNumberField("byte_count", entry.getByteCount().getValue());                        
+            jGen.writeFieldName("bucket_counters");
+            jGen.writeStartArray();            
+            for (OFBucketCounter bCounter : entry.getBucketStats()) {
+                jGen.writeStartObject();
+                jGen.writeNumberField("packet_count", bCounter.getPacketCount().getValue());
+                jGen.writeNumberField("byte_count", bCounter.getByteCount().getValue());
+                jGen.writeEndObject();
+            }//end of for loop - BucketCounter
+            jGen.writeEndArray();
+            if (OFVersion.OF_13 == entry.getVersion()) {
+                jGen.writeNumberField("duration_sec", entry.getDurationSec());
+                jGen.writeNumberField("duration_nsec", entry.getDurationNsec());
+            }
+            jGen.writeEndObject();
+        }//end of for loop - groupStats
+        jGen.writeEndArray();
+    }
+
+    /***
+     * Serializes Group Desc Reply
+     * @author Naveen
+     * @param groupDescReplies
+     * @param jGen
+     * @throws IOException
+     * @throws JsonProcessingException
+     */
+    public static void serializeGroupDescReply(List<OFGroupDescStatsReply> groupDescReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+        OFGroupDescStatsReply groupDescReply = groupDescReplies.get(0);
+        jGen.writeStringField("version", groupDescReply.getVersion().toString()); //return the enum name
+        jGen.writeFieldName("group_desc");
+        jGen.writeStartArray();
+        for (OFGroupDescStatsEntry entry : groupDescReply.getEntries()) {
+            jGen.writeStartObject();                        
+            jGen.writeStringField("group_type",entry.getGroupType().toString());
+            jGen.writeStringField("group_number",entry.getGroup().toString());                                               
+            jGen.writeFieldName("buckets");            
+            jGen.writeStartArray();            
+            for (OFBucket buckets : entry.getBuckets()) {            	
+                jGen.writeStartObject();
+                jGen.writeNumberField("weight", buckets.getWeight());
+                jGen.writeNumberField("watch_port", buckets.getWatchPort().getPortNumber());
+                jGen.writeStringField("watch_group", buckets.getWatchGroup().toString());            	
+                OFActionListSerializer.serializeActions(jGen, buckets.getActions());            	
+                jGen.writeEndObject();
+            }//End of for loop - buckets
+            jGen.writeEndArray();//end of buckets            
+            jGen.writeEndObject();//end of group Desc iteration
+        }//End of for loop - GroupDescStats
+        jGen.writeEndArray();//end of group Desc
+    }
+
+    /***
+     * Serializes Group Feature Reply 
+     * @author Naveen
+     * @param groupFeaturesReplies
+     * @param jGen
+     * @throws IOException
+     * @throws JsonProcessingException
+     */
+    public static void serializeGroupFeaturesReply(List<OFGroupFeaturesStatsReply> groupFeaturesReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+
+        OFGroupFeaturesStatsReply groupFeaturesReply = groupFeaturesReplies.get(0);
+        jGen.writeStringField("version", groupFeaturesReply.getVersion().toString()); //return the enum name
+
+        jGen.writeFieldName("group_features");
+        jGen.writeStartObject();                        
+        jGen.writeArrayFieldStart("capabilities");
+        for (OFGroupCapabilities c : groupFeaturesReply.getCapabilities()) {
+            jGen.writeString(c.toString());
+        }
+        jGen.writeEndArray();
+        jGen.writeNumberField("max_groups_all", groupFeaturesReply.getMaxGroupsAll());
+        jGen.writeNumberField("max_groups_select", groupFeaturesReply.getMaxGroupsSelect());
+        jGen.writeNumberField("max_groups_indirect", groupFeaturesReply.getMaxGroupsIndirect());
+        jGen.writeNumberField("max_groups_ff", groupFeaturesReply.getMaxGroupsFf());
+        jGen.writeNumberField("actions_all", groupFeaturesReply.getActionsAll());
+        jGen.writeNumberField("actions_select", groupFeaturesReply.getActionsSelect());
+        jGen.writeNumberField("actions_indirect", groupFeaturesReply.getActionsIndirect());
+        jGen.writeNumberField("actions_ff", groupFeaturesReply.getActionsFf());
+
+        jGen.writeEndObject();//end of group Feature
+    }
+
+    /***
+     * Serializes the Meter Statistics Reply
+     * @author Naveen
+     * @param meterReplies
+     * @param jGen
+     * @throws IOException
+     * @throws JsonProcessingException
+     */
+    public static void serializeMeterReply(List<OFMeterStatsReply> meterReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+        OFMeterStatsReply meterReply = meterReplies.get(0); // we will get only one meterReply and it will contains many OFMeterStatsEntry ?
+        jGen.writeStringField("version", meterReply.getVersion().toString()); //return the enum name
+        jGen.writeFieldName("meter");
+        jGen.writeStartArray();
+        for (OFMeterStats entry : meterReply.getEntries()) {
+            jGen.writeStartObject();
+            jGen.writeNumberField("meter_id", entry.getMeterId());                        
+            jGen.writeNumberField("flow_count", entry.getFlowCount());
+            jGen.writeNumberField("packet_in_count", entry.getPacketInCount().getValue());
+            jGen.writeNumberField("byte_in_count", entry.getByteInCount().getValue());
+            jGen.writeFieldName("meter_band_stats");
+            jGen.writeStartArray();
+            for (OFMeterBandStats bandStats : entry.getBandStats()) {
+                jGen.writeStartObject();
+                jGen.writeNumberField("packet_band_count", bandStats.getPacketBandCount().getValue());
+                jGen.writeNumberField("byte_band_count", bandStats.getByteBandCount().getValue());
+                jGen.writeEndObject();
+            }//End of for loop - bandStats
+            jGen.writeEndArray();          
+
+            jGen.writeNumberField("duration_sec", entry.getDurationSec());
+            jGen.writeNumberField("duration_nssec", entry.getDurationNsec());            
+            jGen.writeEndObject();
+        }//End of for loop - MeterStats
+        jGen.writeEndArray();
+    }
+
+    /***
+     * Serializes Meter Feature Reply
+     * @author Naveen
+     * @param meterFeaturesReplies
+     * @param jGen
+     * @throws IOException
+     * @throws JsonProcessingException
+     */
+    public static void serializeMeterFeaturesReply(List<OFMeterFeaturesStatsReply> meterFeaturesReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+        OFMeterFeaturesStatsReply meterFeaturesReply = meterFeaturesReplies.get(0);
+        jGen.writeStringField("version", meterFeaturesReply.getVersion().toString()); //return the enum name
+
+        OFMeterFeatures meterFeatures = meterFeaturesReply.getFeatures();
+        jGen.writeFieldName("meter_features");
+        jGen.writeStartObject();      
+
+        jGen.writeNumberField("max_meters", meterFeatures.getMaxMeter());
+        jGen.writeNumberField("band_types", meterFeatures.getBandTypes());
+        jGen.writeNumberField("capabilities", meterFeatures.getCapabilities());
+        jGen.writeNumberField("max_bands", meterFeatures.getMaxBands());
+        jGen.writeNumberField("max_colors", meterFeatures.getMaxColor());
+
+        jGen.writeEndObject();//end of group Feature
+    }
+
+    /***
+     * Serializes Meter Config Reply
+     * @author Naveen 
+     * @param meterConfigReplies
+     * @param jGen
+     * @throws IOException
+     * @throws JsonProcessingException
+     */
+    public static void serializeMeterConfigReply(List<OFMeterConfigStatsReply> meterConfigReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+        OFMeterConfigStatsReply meterConfigReply = meterConfigReplies.get(0);
+        jGen.writeStringField("version", meterConfigReply.getVersion().toString()); //return the enum name
+        jGen.writeFieldName("meter_config");
+        jGen.writeStartArray();
+        for (OFMeterConfig config : meterConfigReply.getEntries()) {
+            jGen.writeStartObject();
+            jGen.writeNumberField("meter_id", config.getMeterId());
+            jGen.writeArrayFieldStart("flags");
+            for (OFMeterFlags f : config.getFlags()) {
+                jGen.writeString(f.toString());
+            }
+            jGen.writeEndArray();
+            jGen.writeFieldName("meter_bands");
+            jGen.writeStartArray();
+            for (OFMeterBand band : config.getEntries()) {
+                jGen.writeStartObject();
+                int type = band.getType();
+                jGen.writeNumberField("band_type", type);
+
+                switch (type) {
+                case OFMeterBandTypeSerializerVer15.DROP_VAL:
+                    OFMeterBandDrop bandDrop = (OFMeterBandDrop) band;
+                    jGen.writeNumberField("rate", bandDrop.getRate());
+                    jGen.writeNumberField("burst_size", bandDrop.getBurstSize());
+                    break;
+
+                case OFMeterBandTypeSerializerVer15.DSCP_REMARK_VAL:
+                    OFMeterBandDscpRemark bandDscp = (OFMeterBandDscpRemark) band;
+                    jGen.writeNumberField("rate", bandDscp.getRate());
+                    jGen.writeNumberField("burst_size", bandDscp.getBurstSize());
+                    jGen.writeNumberField("prec_level", bandDscp.getPrecLevel());
+                    break;
+
+                case OFMeterBandTypeSerializerVer15.EXPERIMENTER_VAL:
+                    OFMeterBandExperimenter bandExp = (OFMeterBandExperimenter) band;
+                    jGen.writeNumberField("rate", bandExp.getRate());
+                    jGen.writeNumberField("burst_size", bandExp.getBurstSize());
+                    jGen.writeNumberField("experimenter", bandExp.getExperimenter());
+                    break;
+
+                default:
+                    // shouldn't ever get here
+                    break;            		
+                }//end of Switch Case
+
+                jGen.writeEndObject();
+            }//end of for loop
+            jGen.writeEndArray();
+            jGen.writeEndObject();
+        }//end of for loop
+        jGen.writeEndArray();
+    }
+
+    /***
+     * Serializes Table Statistics
+     * @author Naveen
+     * @param tableReplies
+     * @param jGen
+     * @throws IOException
+     * @throws JsonProcessingException
+     */
+    public static void serializeTableReply(List<OFTableStatsReply> tableReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+
+        OFTableStatsReply tableReply = tableReplies.get(0); // we will get only one tableReply and it will contains many OFTableStatsEntry ?
+        jGen.writeStringField("version", tableReply.getVersion().toString()); //return the enum name
+        jGen.writeFieldName("table");
+        jGen.writeStartArray();
+        for (OFTableStatsEntry entry : tableReply.getEntries()) {
+            jGen.writeStartObject();
+
+            //Fields common to all OF versions
+            //For OF 1.3, only these fields are applicable
+            jGen.writeStringField("table_id",entry.getTableId().toString());                        
+            jGen.writeNumberField("active_count", entry.getActiveCount());
+            jGen.writeNumberField("lookup_count", entry.getLookupCount().getValue());
+            jGen.writeNumberField("match_count", entry.getMatchedCount().getValue());
+
+            //Fields Applicable only for specific Versions
+            switch (entry.getVersion()) {   
+            case OF_15:
+            case OF_14:
+                break;
+            case OF_13:
+            case OF_12:
+                //Fields applicable only to OF 1.2+
+                jGen.writeNumberField("write_set_fields", entry.getWriteSetfields().getValue());
+                jGen.writeNumberField("apply_set_fields", entry.getApplySetfields().getValue());
+                jGen.writeNumberField("metadata_match", entry.getMetadataMatch().getValue());
+                jGen.writeNumberField("metadata_write", entry.getMetadataWrite().getValue());            
+            case OF_11:
+                //Fields applicable to OF 1.1 & 1.2
+                jGen.writeStringField("match", entry.getMatch().toString());
+                jGen.writeNumberField("instructions", entry.getInstructions());
+                jGen.writeNumberField("write_actions", entry.getWriteActions());
+                jGen.writeNumberField("apply_actions", entry.getApplyActions());
+                jGen.writeNumberField("config", entry.getConfig());            	
+            case OF_10:
+                //Fields applicable to OF 1.0, 1.1 & 1.2 
+                jGen.writeStringField("name",entry.getName());                        
+                jGen.writeNumberField("wildcards", entry.getWildcards());
+                jGen.writeNumberField("max_entries", entry.getMaxEntries());
+                break;                   
+            default:
+                break;            	
+            } //End of switch case
+            jGen.writeEndObject();
+        } //End of for loop
+        jGen.writeEndArray();
+    }
+
+    /***
+     * Serializes Table Features Reply
+     * @author Naveen
+     * @param tableFeaturesReplies
+     * @param jGen
+     * @throws IOException
+     * @throws JsonProcessingException
+     */
+    public static void serializeTableFeaturesReply(List<OFTableFeaturesStatsReply> tableFeaturesReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+
+        jGen.writeFieldName("tableFeatures");
+        jGen.writeStartArray();
+        for (OFTableFeaturesStatsReply tableFeaturesReply : tableFeaturesReplies) {
+
+            for (OFTableFeatures tableFeature : tableFeaturesReply.getEntries()) {
+                jGen.writeStartObject();    
+                jGen.writeStringField("version", tableFeature.getVersion().toString());
+                jGen.writeNumberField("table_id", tableFeature.getTableId().getValue());
+                jGen.writeStringField("name", tableFeature.getName());
+                jGen.writeNumberField("metadata_match", tableFeature.getMetadataMatch().getValue());
+                jGen.writeNumberField("metadata_write", tableFeature.getMetadataWrite().getValue());
+                jGen.writeNumberField("config", tableFeature.getConfig());
+                jGen.writeNumberField("max_entries", tableFeature.getMaxEntries());
+
+                jGen.writeFieldName("properties");
+                jGen.writeStartArray();
+                for (OFTableFeatureProp properties : tableFeature.getProperties()) {            	
+                    jGen.writeStartObject();
+
+                    int type = properties.getType();
+                    switch (type) {
+                    case OFTableFeaturePropTypeSerializerVer15.INSTRUCTIONS_VAL:
+                        OFTableFeaturePropInstructions propInstruct = (OFTableFeaturePropInstructions) properties;
+                        jGen.writeFieldName("instructions");
+                        jGen.writeStartArray();
+                        for (OFInstructionId id : propInstruct.getInstructionIds()) {
+                            jGen.writeString(id.getType().toString());              			
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.INSTRUCTIONS_MISS_VAL:
+                        OFTableFeaturePropInstructionsMiss propInstructMiss = (OFTableFeaturePropInstructionsMiss) properties;
+                        jGen.writeFieldName("instructions_miss");
+                        jGen.writeStartArray();
+                        for (OFInstructionId id : propInstructMiss.getInstructionIds()) {
+                            jGen.writeString(id.getType().toString());              			
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.NEXT_TABLES_VAL:
+                        OFTableFeaturePropNextTables propNxtTables = (OFTableFeaturePropNextTables) properties;
+                        jGen.writeFieldName("next_tables");
+                        jGen.writeStartArray();
+                        for (U8 id : propNxtTables.getNextTableIds()) {
+                            jGen.writeNumber(id.getValue());
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.NEXT_TABLES_MISS_VAL:
+                        OFTableFeaturePropNextTablesMiss propNxtTablesMiss = (OFTableFeaturePropNextTablesMiss) properties;
+                        jGen.writeFieldName("next_tables_miss");
+                        jGen.writeStartArray();
+                        for (U8 id : propNxtTablesMiss.getNextTableIds()) {
+                            jGen.writeNumber(id.getValue());
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.WRITE_ACTIONS_VAL:
+                        OFTableFeaturePropWriteActions propWrAct = (OFTableFeaturePropWriteActions) properties; 
+                        jGen.writeFieldName("write_actions");
+                        jGen.writeStartArray();
+                        for (OFActionId id : propWrAct.getActionIds()) {
+                            jGen.writeString(id.getType().toString());
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.WRITE_ACTIONS_MISS_VAL:
+                        OFTableFeaturePropWriteActionsMiss propWrActMiss = (OFTableFeaturePropWriteActionsMiss) properties;
+                        jGen.writeFieldName("write_actions_miss");
+                        jGen.writeStartArray();
+                        for (OFActionId id : propWrActMiss.getActionIds()) {
+                            jGen.writeString(id.getType().toString());
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.APPLY_ACTIONS_VAL:
+                        OFTableFeaturePropApplyActions propAppAct = (OFTableFeaturePropApplyActions) properties;   
+                        jGen.writeFieldName("apply_actions");
+                        jGen.writeStartArray();
+                        for (OFActionId id : propAppAct.getActionIds()) {
+                            jGen.writeString(id.getType().toString());
+                        }
+                        jGen.writeEndArray();
+                        break;	
+                    case OFTableFeaturePropTypeSerializerVer15.APPLY_ACTIONS_MISS_VAL:
+                        OFTableFeaturePropApplyActionsMiss propAppActMiss = (OFTableFeaturePropApplyActionsMiss) properties;
+                        jGen.writeFieldName("apply_actions_miss");
+                        jGen.writeStartArray();
+                        for (OFActionId id : propAppActMiss.getActionIds()) {
+                            jGen.writeString(id.getType().toString());
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.MATCH_VAL:                	
+                        OFTableFeaturePropMatch propMatch = (OFTableFeaturePropMatch) properties;
+                        jGen.writeFieldName("match");
+                        jGen.writeStartArray();
+                        for (U32 id : propMatch.getOxmIds()) {
+                            jGen.writeString(OXMUtils.oxmIdToString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.WILDCARDS_VAL:
+                        OFTableFeaturePropWildcards propWildcards = (OFTableFeaturePropWildcards) properties;
+                        jGen.writeFieldName("wildcards");
+                        jGen.writeStartArray();
+                        for (U32 id : propWildcards.getOxmIds()) {
+                            jGen.writeString(OXMUtils.oxmIdToString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.WRITE_SETFIELD_VAL:
+                        OFTableFeaturePropWriteSetfield propWrSetfield = (OFTableFeaturePropWriteSetfield) properties;           
+                        jGen.writeFieldName("write_set_field");
+                        jGen.writeStartArray();
+                        for (U32 id : propWrSetfield.getOxmIds()) {
+                            jGen.writeString(OXMUtils.oxmIdToString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.WRITE_SETFIELD_MISS_VAL:
+                        OFTableFeaturePropWriteSetfieldMiss propWrSetfieldMiss = (OFTableFeaturePropWriteSetfieldMiss) properties; 
+                        jGen.writeFieldName("write_set_field_miss");
+                        jGen.writeStartArray();
+                        for (U32 id : propWrSetfieldMiss.getOxmIds()) {
+                            jGen.writeString(OXMUtils.oxmIdToString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.APPLY_SETFIELD_VAL:
+                        OFTableFeaturePropApplySetfield propAppSetfield = (OFTableFeaturePropApplySetfield) properties;
+                        jGen.writeFieldName("apply_set_field");
+                        jGen.writeStartArray();
+                        for (U32 id : propAppSetfield.getOxmIds()) {
+                            jGen.writeString(OXMUtils.oxmIdToString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.APPLY_SETFIELD_MISS_VAL:
+                        OFTableFeaturePropApplySetfieldMiss propAppSetfieldMiss = (OFTableFeaturePropApplySetfieldMiss) properties;                		
+                        jGen.writeFieldName("apply_set_field_miss");
+                        jGen.writeStartArray();
+                        for (U32 id : propAppSetfieldMiss.getOxmIds()) {
+                            jGen.writeString(OXMUtils.oxmIdToString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.EXPERIMENTER_VAL:
+                        OFTableFeaturePropExperimenter propExp = (OFTableFeaturePropExperimenter) properties; 
+                        jGen.writeFieldName("experimenter");
+                        jGen.writeStartObject();
+                        jGen.writeNumberField("sub_type", propExp.getSubtype());
+                        jGen.writeNumberField("experimenter", propExp.getExperimenter());
+                        jGen.writeStringField("data", propExp.getExperimenterData().toString());
+                        jGen.writeEndObject();
+                        break;	
+                    case OFTableFeaturePropTypeSerializerVer15.EXPERIMENTER_MISS_VAL:
+                        OFTableFeaturePropExperimenterMiss propExpMiss = (OFTableFeaturePropExperimenterMiss) properties;
+                        jGen.writeFieldName("experimenter_miss");
+                        jGen.writeStartObject();
+                        jGen.writeNumberField("sub_type", propExpMiss.getSubtype());
+                        jGen.writeNumberField("experimenter", propExpMiss.getExperimenter());
+                        jGen.writeStringField("data", propExpMiss.getExperimenterData().toString());
+                        jGen.writeEndObject();
+                        break;	
+                    case OFTableFeaturePropTypeSerializerVer15.APPLY_COPYFIELD_MISS_VAL:
+                        OFTableFeaturePropApplyCopyfieldMiss propApplyCopyfieldMiss = (OFTableFeaturePropApplyCopyfieldMiss) properties;           
+                        jGen.writeFieldName("apply_copy_field_miss");
+                        jGen.writeStartArray();
+                        for (U32 id : propApplyCopyfieldMiss.getOxmIds()) {
+                            jGen.writeString(OXMUtils.oxmIdToString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.APPLY_COPYFIELD_VAL:
+                        OFTableFeaturePropApplyCopyfield propApplyCopyfield = (OFTableFeaturePropApplyCopyfield) properties;           
+                        jGen.writeFieldName("apply_copy_field");
+                        jGen.writeStartArray();
+                        for (U32 id : propApplyCopyfield.getOxmIds()) {
+                            jGen.writeString(OXMUtils.oxmIdToString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.WRITE_COPYFIELD_MISS_VAL:
+                        OFTableFeaturePropWriteCopyfieldMiss propWriteCopyfieldMiss = (OFTableFeaturePropWriteCopyfieldMiss) properties;           
+                        jGen.writeFieldName("write_copy_field_miss");
+                        jGen.writeStartArray();
+                        for (U32 id : propWriteCopyfieldMiss.getOxmIds()) {
+                            jGen.writeString(OXMUtils.oxmIdToString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.WRITE_COPYFIELD_VAL:
+                        OFTableFeaturePropWriteCopyfield propWriteCopyfield = (OFTableFeaturePropWriteCopyfield) properties;           
+                        jGen.writeFieldName("write_copy_field");
+                        jGen.writeStartArray();
+                        for (U32 id : propWriteCopyfield.getOxmIds()) {
+                            jGen.writeString(OXMUtils.oxmIdToString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.TABLE_SYNC_FROM_VAL:
+                        OFTableFeaturePropTableSyncFrom propTableSyncFrom = (OFTableFeaturePropTableSyncFrom) properties;           
+                        jGen.writeFieldName("write_sync_table_from");
+                        jGen.writeStartArray();
+                        for (U8 id : propTableSyncFrom.getTableIds()) {
+                            jGen.writeString(id.toString());
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    case OFTableFeaturePropTypeSerializerVer15.PACKET_TYPES_VAL:
+                        OFTableFeaturePropOxmValues propOxmValues = (OFTableFeaturePropOxmValues) properties; /* TODO name mismatch? */        
+                        jGen.writeFieldName("packet_types");
+                        jGen.writeStartArray();
+                        for (byte id : propOxmValues.getOxmValues()) {
+                            jGen.writeString(Byte.toString(id));
+                        }
+                        jGen.writeEndArray();
+                        break;
+                    default:
+                        logger.warn("Unexpected OFTableFeaturePropType value {}", type);
+                        break;            		
+                    }//end of Switch Case  
+                    jGen.writeEndObject();
+                }//end of for loop - properties                                              
+                jGen.writeEndArray();
+                jGen.writeEndObject();
+            }//end of for loop - features
+        } //end of looping through REQ_MORE flagged message loop
+        jGen.writeEndArray();
+    } 
+
+    public static void serializePortReply(List<OFPortStatsReply> portReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{		
+        jGen.writeFieldName("port_reply");
+        jGen.writeStartArray();
+        for (OFPortStatsReply portReply : portReplies) {
+            jGen.writeStartObject();
+            jGen.writeStringField("version", portReply.getVersion().toString()); //return the enum name          
+            jGen.writeFieldName("port");
+            jGen.writeStartArray();
+            for (OFPortStatsEntry entry : portReply.getEntries()) {
+                jGen.writeStartObject();
+                jGen.writeStringField("port_number",entry.getPortNo().toString());
+                jGen.writeNumberField("receive_packets", entry.getRxPackets().getValue());
+                jGen.writeNumberField("transmit_packets", entry.getTxPackets().getValue());
+                jGen.writeNumberField("receive_bytes", entry.getRxBytes().getValue());
+                jGen.writeNumberField("transmit_bytes", entry.getTxBytes().getValue());
+                jGen.writeNumberField("receive_dropped", entry.getRxDropped().getValue());
+                jGen.writeNumberField("transmit_dropped", entry.getTxDropped().getValue());
+                jGen.writeNumberField("receive_errors", entry.getRxErrors().getValue());
+                jGen.writeNumberField("transmit_errors", entry.getTxErrors().getValue());
+                if (entry.getVersion().compareTo(OFVersion.OF_13) <= 0) {
+                    jGen.writeNumberField("receive_frame_errors", entry.getRxFrameErr().getValue());
+                    jGen.writeNumberField("receive_overrun_errors", entry.getRxOverErr().getValue());
+                    jGen.writeNumberField("receive_CRC_errors", entry.getRxCrcErr().getValue());
+                    jGen.writeNumberField("collisions", entry.getCollisions().getValue());
+                }
+                if (entry.getVersion().compareTo(OFVersion.OF_13) >= 0) {
+                    jGen.writeNumberField("duration_sec", entry.getDurationSec());
+                    jGen.writeNumberField("duration_nsec", entry.getDurationNsec());
+                }
+                if (entry.getVersion().compareTo(OFVersion.OF_14) >= 0) {
+                    jGen.writeFieldName("properties");
+                    jGen.writeStartArray();
+                    for (OFPortStatsProp p : entry.getProperties()) {
+                        jGen.writeStartObject();
+                        if (p instanceof OFPortStatsPropEthernet) {
+                            jGen.writeStringField("type", OFPortStatsPropTypeSerializerVer15.ofWireValue((short)p.getType()).name());
+                            jGen.writeNumberField("collisions", ((OFPortStatsPropEthernet) p).getCollisions().getValue());
+                            jGen.writeNumberField("rx_crc_error", ((OFPortStatsPropEthernet) p).getRxCrcErr().getValue());
+                            jGen.writeNumberField("rx_frame_error", ((OFPortStatsPropEthernet) p).getRxFrameErr().getValue());
+                            jGen.writeNumberField("rx_over_error", ((OFPortStatsPropEthernet) p).getRxOverErr().getValue());
+                        } else if (p instanceof OFPortStatsPropOptical) {
+                            jGen.writeStringField("type", OFPortStatsPropTypeSerializerVer15.ofWireValue((short)p.getType()).name());
+                            jGen.writeNumberField("bias_current", ((OFPortStatsPropOptical) p).getBiasCurrent());
+                            jGen.writeNumberField("flags", ((OFPortStatsPropOptical) p).getFlags());
+                            jGen.writeNumberField("rx_freq_lambda", ((OFPortStatsPropOptical) p).getRxFreqLmda());
+                            jGen.writeNumberField("rx_grid_span", ((OFPortStatsPropOptical) p).getRxGridSpan());
+                            jGen.writeNumberField("rx_offset", ((OFPortStatsPropOptical) p).getRxOffset());
+                            jGen.writeNumberField("rx_power", ((OFPortStatsPropOptical) p).getRxPwr());
+                            jGen.writeNumberField("rx_temp", ((OFPortStatsPropOptical) p).getTemperature());
+                            jGen.writeNumberField("tx_freq_lambda", ((OFPortStatsPropOptical) p).getTxFreqLmda());
+                            jGen.writeNumberField("tx_grid_span", ((OFPortStatsPropOptical) p).getTxGridSpan());
+                            jGen.writeNumberField("tx_offset", ((OFPortStatsPropOptical) p).getTxOffset());
+                            jGen.writeNumberField("tx_power", ((OFPortStatsPropOptical) p).getTxPwr());
+                        } else if (p instanceof OFPortStatsPropExperimenter) {
+                            jGen.writeStringField("type", OFPortStatsPropTypeSerializerVer15.ofWireValue((short)p.getType()).name());
+                        }
+                        jGen.writeEndObject();
+                    }
+                    jGen.writeEndArray();
+                }
+                jGen.writeEndObject();
+            }
+            jGen.writeEndArray();
+            jGen.writeEndObject();
+        }
+        jGen.writeEndArray();
+    }
+
+    public static void serializeFlowReply(List<OFFlowStatsReply> flowReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+        /* start the array before each reply */
+        jGen.writeFieldName("flows"); 
+        jGen.writeStartArray();
+        for (OFFlowStatsReply flowReply : flowReplies) { // for each flow stats reply
+            List<OFFlowStatsEntry> entries = flowReply.getEntries();
+            for (OFFlowStatsEntry entry : entries) { // for each flow
+                jGen.writeStartObject();
+                // list flow stats/info
+                jGen.writeStringField("version", entry.getVersion().toString()); // return the enum name
+                jGen.writeNumberField("cookie", entry.getCookie().getValue());
+                jGen.writeStringField("table_id", entry.getTableId().toString());
+                jGen.writeNumberField("packet_count", entry.getPacketCount().getValue());
+                jGen.writeNumberField("byte_count", entry.getByteCount().getValue());
+                jGen.writeNumberField("duration_sec", entry.getDurationSec());
+                jGen.writeNumberField("duration_nsec", entry.getDurationNsec());
+                jGen.writeNumberField("priority", entry.getPriority());
+                jGen.writeNumberField("idle_timeout_s", entry.getIdleTimeout());
+                jGen.writeNumberField("hard_timeout_s", entry.getHardTimeout());
+                if (entry.getVersion().compareTo(OFVersion.OF_10) != 0) {
+                    jGen.writeArrayFieldStart("flags");
+                    for (OFFlowModFlags f : entry.getFlags()) {
+                        jGen.writeString(f.toString());
+                    }
+                    jGen.writeEndArray();
+                }
+
+                MatchSerializer.serializeMatch(jGen, entry.getMatch());
+
+                // handle OF1.1+ instructions with actions within
+                if (entry.getVersion() == OFVersion.OF_10) {
+                    jGen.writeObjectFieldStart("actions");
+                    OFActionListSerializer.serializeActions(jGen, entry.getActions());
+                    jGen.writeEndObject();
+                } else {
+                    OFInstructionListSerializer.serializeInstructionList(jGen, entry.getInstructions());
+                }
+
+                jGen.writeEndObject();
+            } // end for each OFFlowStatsReply entry */
+        } // end for each OFStatsReply
+        //jGen.writeEndObject();
+        jGen.writeEndArray();
+    } // end method
+
+    public static void serializeDescReply(List<OFDescStatsReply> descReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+        OFDescStatsReply descReply = descReplies.get(0); // There is only one descReply from the switch
+        jGen.writeObjectFieldStart("desc"); 
+        jGen.writeStringField("version", descReply.getVersion().toString()); //return the enum name
+        jGen.writeStringField("manufacturer_description", descReply.getMfrDesc()); 
+        jGen.writeStringField("hardware_description", descReply.getHwDesc()); 
+        jGen.writeStringField("software_description", descReply.getSwDesc()); 
+        jGen.writeStringField("serial_number", descReply.getSerialNum()); 
+        jGen.writeStringField("datapath_description", descReply.getDpDesc()); 
+        jGen.writeEndObject(); // end match
+    }
+
+    public static void serializeAggregateReply(List<OFAggregateStatsReply> aggregateReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+        OFAggregateStatsReply aggregateReply = aggregateReplies.get(0); // There are only one aggregateReply from the switch
+        jGen.writeObjectFieldStart("aggregate"); 
+        jGen.writeStringField("version", aggregateReply.getVersion().toString()); //return the enum name
+        jGen.writeNumberField("flow_count", aggregateReply.getFlowCount());
+        jGen.writeNumberField("packet_count", aggregateReply.getPacketCount().getValue());
+        jGen.writeNumberField("byte_count", aggregateReply.getByteCount().getValue());
+        jGen.writeArrayFieldStart("flags");
+        for (OFStatsReplyFlags f : aggregateReply.getFlags()) {
+            jGen.writeString(f.toString());
+        }
+        jGen.writeEndArray();
+        jGen.writeEndObject(); // end match
+    }
+
+    public static void serializePortDescReply(List<OFPortDescStatsReply> portDescReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{
+        OFPortDescStatsReply portDescReply = portDescReplies.get(0); // we will get only one PortDescReply and it will contains many OFPortDescStatsEntry ?
+        jGen.writeStringField("version", portDescReply.getVersion().toString()); //return the enum name
+        serializePortDesc(portDescReply.getEntries(), jGen);
+    }
+
+    public static void serializePortDesc(List<OFPortDesc> portDescList, JsonGenerator jGen) throws IOException, JsonProcessingException {
+        jGen.writeFieldName("port_desc");
+        jGen.writeStartArray();
+        for(OFPortDesc entry : portDescList) {
+            jGen.writeStartObject();
+            jGen.writeStringField("port_number",entry.getPortNo().toString());
+            jGen.writeStringField("hardware_address", entry.getHwAddr().toString());
+            jGen.writeStringField("name", entry.getName());
+            jGen.writeArrayFieldStart("config");
+            for (OFPortConfig e : entry.getConfig()) {
+                jGen.writeString(e.toString());
+            }
+            jGen.writeEndArray();
+            jGen.writeArrayFieldStart("state");
+            for (OFPortState e : entry.getState()) {
+                jGen.writeString(e.toString());
+            }
+            jGen.writeEndArray();
+
+            if (entry.getVersion().compareTo(OFVersion.OF_13) <= 0) {
+                jGen.writeArrayFieldStart("current_features");
+                for (OFPortFeatures e : entry.getCurr()) {
+                    jGen.writeString(e.toString());
+                }
+                jGen.writeEndArray();
+                jGen.writeArrayFieldStart("advertised_features");
+                for (OFPortFeatures e : entry.getAdvertised()) {
+                    jGen.writeString(e.toString());
+                }
+                jGen.writeEndArray();
+                jGen.writeArrayFieldStart("supported_features");
+                for (OFPortFeatures e : entry.getSupported()) {
+                    jGen.writeString(e.toString());
+                }
+                jGen.writeEndArray();
+                jGen.writeArrayFieldStart("peer_features");
+                for (OFPortFeatures e : entry.getPeer()) {
+                    jGen.writeString(e.toString());
+                }
+                jGen.writeEndArray();
+            } else if (entry.getVersion().compareTo(OFVersion.OF_14) >= 0) {
+                jGen.writeArrayFieldStart("properties");
+                for (OFPortDescProp e : entry.getProperties()) {
+                    jGen.writeString(OFPortDescPropTypeSerializerVer15.ofWireValue((short) e.getType()).toString());
+                }
+                jGen.writeEndArray();
+            }
+
+            if (entry.getVersion().compareTo(OFVersion.OF_11) >= 0 && 
+                    entry.getVersion().compareTo(OFVersion.OF_13) <= 0) {
+                jGen.writeNumberField("curr_speed",entry.getCurrSpeed());
+                jGen.writeNumberField("max_speed",entry.getMaxSpeed());
+            }
+            jGen.writeEndObject();
+        }
+        jGen.writeEndArray();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/debugevent/CustomFormatter.java b/src/main/java/net/floodlightcontroller/debugevent/CustomFormatter.java
deleted file mode 100644
index 947a291a1f33f6e6e0611f02ef4e3f2fe1ea5c77..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/debugevent/CustomFormatter.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import net.floodlightcontroller.debugevent.EventResource.EventResourceBuilder;
-
-/**
- * Format Event object based on its class and store accordingly in
- * {@link EventResource}
- */
-public interface CustomFormatter<T> {
-
-    public abstract EventResourceBuilder
-            customFormat(T obj, String name, EventResourceBuilder edb);
-}
diff --git a/src/main/java/net/floodlightcontroller/debugevent/CustomFormatters.java b/src/main/java/net/floodlightcontroller/debugevent/CustomFormatters.java
deleted file mode 100644
index 6685461e10a68e54d52658756eebd2c77e8a4132..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/debugevent/CustomFormatters.java
+++ /dev/null
@@ -1,221 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import java.lang.ref.SoftReference;
-import java.util.Collection;
-
-import javax.annotation.Nullable;
-
-import net.floodlightcontroller.debugevent.EventResource.EventResourceBuilder;
-import net.floodlightcontroller.debugevent.EventResource.Metadata;
-import net.floodlightcontroller.devicemanager.SwitchPort;
-
-import org.projectfloodlight.openflow.types.DatapathId;
-import org.projectfloodlight.openflow.types.IPv4Address;
-import org.projectfloodlight.openflow.util.HexString;
-
-import net.floodlightcontroller.packet.IPv4;
-
-import com.google.common.base.Joiner;
-
-class CustomFormatterCollectionAttachmentPoint implements
-    CustomFormatter<Collection<SwitchPort>> {
-
-    @Override
-    public EventResourceBuilder
-            customFormat(@Nullable Collection<SwitchPort> aps2, String name,
-                         EventResourceBuilder edb) {
-        if (aps2 != null) {
-            StringBuilder apsStr2 = new StringBuilder();
-            if (aps2.size() == 0) {
-                apsStr2.append("--");
-            } else {
-                for (SwitchPort ap : aps2) {
-                    apsStr2.append(ap.getSwitchDPID().toString());
-                    apsStr2.append("/");
-                    apsStr2.append(ap.getPort());
-                    apsStr2.append(" ");
-                }
-                // remove trailing space
-                apsStr2.deleteCharAt(apsStr2.length());
-            }
-            edb.dataFields.add(new Metadata(name, apsStr2.toString()));
-        }
-        return edb;
-    }
-}
-
-class CustomFormatterCollectionIpv4 implements
-    CustomFormatter<Collection<IPv4Address>> {
-
-    @Override
-    public EventResourceBuilder
-            customFormat(@Nullable Collection<IPv4Address> ipv4Addresses2,
-                         String name, EventResourceBuilder edb) {
-        if (ipv4Addresses2 != null) {
-            String ipv4AddressesStr2 = "--";
-            if (!ipv4Addresses2.isEmpty()) {
-                ipv4AddressesStr2 = Joiner.on(" ").join(ipv4Addresses2);
-            }
-            edb.dataFields.add(new Metadata(name, ipv4AddressesStr2));
-        }
-        return edb;
-    }
-}
-
-class CustomFormatterCollectionObject implements
-    CustomFormatter<Collection<Object>> {
-
-    @Override
-    public EventResourceBuilder
-            customFormat(@Nullable Collection<Object> obl2, String name,
-                         EventResourceBuilder edb) {
-        if (obl2 != null) {
-            StringBuilder sbldr2 = new StringBuilder();
-            if (obl2.size() == 0) {
-                sbldr2.append("--");
-            } else {
-                for (Object o : obl2) {
-                    sbldr2.append(o.toString());
-                    sbldr2.append(" ");
-                }
-            }
-            edb.dataFields.add(new Metadata(name, sbldr2.toString()));
-        }
-        return edb;
-    }
-}
-
-class CustomFormatterDpid implements CustomFormatter<DatapathId> {
-
-    @Override
-    public EventResourceBuilder customFormat(@Nullable DatapathId dpid,
-                                             String name,
-                                             EventResourceBuilder edb) {
-        if (dpid != null) {
-            edb.dataFields.add(new Metadata(name, dpid.toString()));
-        }
-        return edb;
-    }
-
-}
-
-class CustomFormatterIpv4 implements CustomFormatter<Integer> {
-
-    @Override
-    public EventResourceBuilder customFormat(@Nullable Integer obj,
-                                             String name,
-                                             EventResourceBuilder edb) {
-        if (obj != null) {
-            edb.dataFields.add(new Metadata(name, IPv4.fromIPv4Address(obj)));
-        }
-        return edb;
-    }
-
-}
-
-class CustomFormatterMac implements CustomFormatter<Long> {
-
-    @Override
-    public EventResourceBuilder customFormat(@Nullable Long obj,
-                                             String name,
-                                             EventResourceBuilder edb) {
-        if (obj != null) {
-            edb.dataFields.add(new Metadata(name, HexString.toHexString(obj,
-                                                                        6)));
-        }
-        return edb;
-    }
-
-}
-
-class CustomFormatterObject implements CustomFormatter<Object> {
-
-    @Override
-    public EventResourceBuilder customFormat(@Nullable Object obj,
-                                             String name,
-                                             EventResourceBuilder edb) {
-        if (obj != null) {
-            edb.dataFields.add(new Metadata(name, obj.toString()));
-        }
-        return edb;
-    }
-
-}
-
-class CustomFormatterPrimitive implements CustomFormatter<Object> {
-
-    @Override
-    public EventResourceBuilder customFormat(@Nullable Object obj,
-                                             String name,
-                                             EventResourceBuilder edb) {
-        if (obj != null) {
-            edb.dataFields.add(new Metadata(name, obj.toString()));
-        }
-        return edb;
-    }
-
-}
-
-class CustomFormatterSrefCollectionObject implements
-    CustomFormatter<SoftReference<Collection<Object>>> {
-
-    @Override
-    public EventResourceBuilder customFormat(@Nullable
-                                         SoftReference<Collection<Object>> srefCollectionObj2,
-                                         String name, EventResourceBuilder edb) {
-        if (srefCollectionObj2 != null) {
-            Collection<Object> ol2 = srefCollectionObj2.get();
-            if (ol2 != null) {
-                StringBuilder sb = new StringBuilder();
-                if (ol2.size() == 0) {
-                    sb.append("--");
-                } else {
-                    for (Object o : ol2) {
-                        sb.append(o.toString());
-                        sb.append(" ");
-                    }
-                }
-                edb.dataFields.add(new Metadata(name, sb.toString()));
-            } else {
-                edb.dataFields.add(new Metadata(name,
-                                                "-- reference not available --"));
-            }
-        }
-        return edb;
-    }
-
-}
-
-class CustomFormatterSrefObject implements CustomFormatter<SoftReference<Object>> {
-
-    @Override
-    public EventResourceBuilder
-            customFormat(@Nullable SoftReference<Object> srefObj,
-                         String name, EventResourceBuilder edb) {
-        if (srefObj != null) {
-            Object o = srefObj.get();
-            if (o != null) {
-                edb.dataFields.add(new Metadata(name, o.toString()));
-            } else {
-                edb.dataFields.add(new Metadata(name,
-                                                "-- reference not available --"));
-            }
-        }
-        return edb;
-    }
-
-}
-
-class CustomFormatterString implements CustomFormatter<String> {
-
-    @Override
-    public EventResourceBuilder customFormat(@Nullable String string,
-                                             String name,
-                                             EventResourceBuilder edb) {
-        if (string != null) {
-            edb.dataFields.add(new Metadata(name, string));
-        }
-        return edb;
-    }
-
-}
diff --git a/src/main/java/net/floodlightcontroller/debugevent/DebugEventAppender.java b/src/main/java/net/floodlightcontroller/debugevent/DebugEventAppender.java
deleted file mode 100644
index ea44f9af18a800fde6e84742cf28965bb8878b89..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/debugevent/DebugEventAppender.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventType;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.UnsynchronizedAppenderBase;
-
-public class DebugEventAppender<E> extends UnsynchronizedAppenderBase<E> {
-    static IDebugEventService debugEvent;
-    static IEventCategory<WarnErrorEvent> evWarnError;
-    static final Thread debugEventRegistryTask;
-    static {
-        debugEventRegistryTask = new Thread() {
-            @Override
-            public void run() {
-                while (DebugEventAppender.debugEvent == null) {
-                    try {
-                        Thread.sleep(100);
-                    } catch (InterruptedException e) {
-                        return;
-                    }
-                }
-                // safe to register debugEvent
-                registerDebugEventQueue();
-            }
-        };
-        debugEventRegistryTask.setDaemon(true);
-    }
-
-    @Override
-    public void start() {
-        DebugEventAppender.debugEventRegistryTask.start();
-        super.start();
-    }
-
-    public static void
-            setDebugEventServiceImpl(IDebugEventService debugEvent) {
-        DebugEventAppender.debugEvent = debugEvent;
-        // It is now ok to register an event Q - but letting this thread go
-        // since it was called from a startUp() routine
-    }
-
-    /**
-     * The logging system calls append for every log message. This method
-     * filters out the WARN and ERROR message and adds to a debug event queue
-     * that can be accessed via cli or rest-api or gui.
-     */
-    @Override
-    protected void append(E eventObject) {
-        if (!isStarted()) {
-            return;
-        }
-        if (evWarnError != null) {
-            ILoggingEvent ev = ((ILoggingEvent) eventObject);
-            if (ev.getLevel().equals(Level.ERROR)
-                || ev.getLevel().equals(Level.WARN)) {
-                evWarnError
-                .newEventWithFlush(new WarnErrorEvent(ev.getFormattedMessage(),
-                                                      ev.getLevel(),
-                                                      ev.getThreadName(),
-                                                      ev.getLoggerName()));
-            }
-        }
-    }
-
-    private static void registerDebugEventQueue() {
-        evWarnError = debugEvent.buildEvent(WarnErrorEvent.class)
-                .setModuleName("net.floodlightcontroller.core")
-                .setEventName("warn-error-queue")
-                .setEventDescription("all WARN and ERROR logs")
-                .setEventType(EventType.ALWAYS_LOG)
-                .setBufferCapacity(100)
-                .setAckable(false)
-                .register();
-
-    }
-
-    public static class WarnErrorEvent {
-        @EventColumn(name = "message", description = EventFieldType.STRING)
-        String message;
-
-        @EventColumn(name = "level", description = EventFieldType.OBJECT)
-        Level level;
-
-        @EventColumn(name = "threadName",
-                     description = EventFieldType.STRING)
-        String threadName;
-
-        @EventColumn(name = "logger", description = EventFieldType.OBJECT)
-        String logger;
-
-        public WarnErrorEvent(String message, Level level,
-                              String threadName, String logger) {
-            this.message = message;
-            this.level = level;
-            this.threadName = threadName;
-            this.logger = logger;
-        }
-    }
-
-}
diff --git a/src/main/java/net/floodlightcontroller/debugevent/DebugEventResource.java b/src/main/java/net/floodlightcontroller/debugevent/DebugEventResource.java
deleted file mode 100644
index ff93c405e9d1ab1c274701de11bbadd3c3d21b1e..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/debugevent/DebugEventResource.java
+++ /dev/null
@@ -1,196 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import java.util.List;
-
-import javax.annotation.concurrent.Immutable;
-
-import net.floodlightcontroller.debugevent.DebugEventService.EventInfo;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.ImmutableList;
-
-/**
- * All the Immutable Resource classes for REST API.
- */
-@Immutable
-public class DebugEventResource {
-
-    protected static final Logger logger = LoggerFactory.getLogger(DebugEventResource.class);
-    public static final String MODULE_NAME_PREDICATE = "module-name";
-    public static final String EVENT_NAME_PREDICATE = "event-name";
-    public static final String LAST_PREDICATE = "num-of-events";
-    public static final String EVENT_ID = "event-id";
-    public static final String EVENT_INSTANCE_ID = "event-instance-id";
-    public static final String ACKED = "acked";
-
-    /**
-     * Resource class for {@link EventInfo}. Used to create Immutable objects
-     * returned in response to REST calls.
-     */
-    @Immutable
-    public static class EventInfoResource implements
-        Comparable<EventInfoResource> {
-
-        private final int eventId;
-        private final boolean enabled;
-        private final int bufferCapacity;
-        private final EventType etype;
-        private final String eventDesc;
-        private final String eventName;
-        private final String moduleName;
-        private final int numOfEvents;
-        private final boolean ackable;
-        public final ImmutableList<EventResource> events;
-
-        public EventInfoResource(EventInfo eventInfo,
-                                 List<EventResource> events) {
-            super();
-            this.eventId = eventInfo.getEventId();
-            this.enabled = eventInfo.isEnabled();
-            this.bufferCapacity = eventInfo.getBufferCapacity();
-            this.etype = eventInfo.getEtype();
-            this.eventDesc = eventInfo.getEventDesc();
-            this.eventName = eventInfo.getEventName();
-            this.moduleName = eventInfo.getModuleName();
-            this.numOfEvents = eventInfo.getNumOfEvents();
-            this.ackable = eventInfo.isAckable();
-            this.events = ImmutableList.copyOf(events);
-        }
-
-        public boolean isEnabled() {
-            return enabled;
-        }
-
-        public int getBufferCapacity() {
-            return bufferCapacity;
-        }
-
-        public EventType getEtype() {
-            return etype;
-        }
-
-        public String getEventDesc() {
-            return eventDesc;
-        }
-
-        public String getEventName() {
-            return eventName;
-        }
-
-        public String getModuleName() {
-            return moduleName;
-        }
-
-        public int getNumOfEvents() {
-            return numOfEvents;
-        }
-
-        public boolean isAckable() {
-            return ackable;
-        }
-
-        public List<EventResource> getEvents() {
-            return events;
-        }
-
-        public int getEventId() {
-            return eventId;
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + (ackable ? 1231 : 1237);
-            result = prime * result + bufferCapacity;
-            result = prime * result + (enabled ? 1231 : 1237);
-            result = prime * result
-                     + ((etype == null) ? 0 : etype.hashCode());
-            result = prime * result
-                     + ((eventDesc == null) ? 0 : eventDesc.hashCode());
-            result = prime * result + eventId;
-            result = prime * result
-                     + ((eventName == null) ? 0 : eventName.hashCode());
-            result = prime * result
-                     + ((events == null) ? 0 : events.hashCode());
-            result = prime * result
-                     + ((moduleName == null) ? 0 : moduleName.hashCode());
-            result = prime * result + numOfEvents;
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) return true;
-            if (obj == null) return false;
-            if (getClass() != obj.getClass()) return false;
-            EventInfoResource other = (EventInfoResource) obj;
-            if (ackable != other.ackable) return false;
-            if (bufferCapacity != other.bufferCapacity) return false;
-            if (enabled != other.enabled) return false;
-            if (etype != other.etype) return false;
-            if (eventDesc == null) {
-                if (other.eventDesc != null) return false;
-            } else if (!eventDesc.equals(other.eventDesc)) return false;
-            if (eventId != other.eventId) return false;
-            if (eventName == null) {
-                if (other.eventName != null) return false;
-            } else if (!eventName.equals(other.eventName)) return false;
-            if (events == null) {
-                if (other.events != null) return false;
-            } else if (!events.equals(other.events)) return false;
-            if (moduleName == null) {
-                if (other.moduleName != null) return false;
-            } else if (!moduleName.equals(other.moduleName)) return false;
-            if (numOfEvents != other.numOfEvents) return false;
-            return true;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-            builder.append("EventInfoResource [eventId=");
-            builder.append(eventId);
-            builder.append(", enabled=");
-            builder.append(enabled);
-            builder.append(", bufferCapacity=");
-            builder.append(bufferCapacity);
-            builder.append(", etype=");
-            builder.append(etype);
-            builder.append(", eventDesc=");
-            builder.append(eventDesc);
-            builder.append(", eventName=");
-            builder.append(eventName);
-            builder.append(", moduleName=");
-            builder.append(moduleName);
-            builder.append(", numOfEvents=");
-            builder.append(numOfEvents);
-            builder.append(", ackable=");
-            builder.append(ackable);
-            builder.append(", events=");
-            builder.append(events);
-            builder.append("]");
-            return builder.toString();
-        }
-
-        /**
-         * The natural order of this class is ascending on eventId and
-         * consistent with equals.
-         */
-        @Override
-        public int compareTo(EventInfoResource o) {
-            return ComparisonChain.start().compare(eventId, o.eventId)
-                                  .compareFalseFirst(enabled, o.enabled)
-                                  .compare(bufferCapacity, o.bufferCapacity)
-                                  .compare(etype, o.etype)
-                                  .compare(eventDesc, o.eventDesc)
-                                  .compare(eventName, o.eventName)
-                                  .compare(moduleName, o.moduleName)
-                                  .compare(numOfEvents, o.numOfEvents)
-                                  .result();
-        }
-    }
-}
diff --git a/src/main/java/net/floodlightcontroller/debugevent/DebugEventService.java b/src/main/java/net/floodlightcontroller/debugevent/DebugEventService.java
deleted file mode 100644
index 47a4ad77bd3358f24f574056c080ddc7b7ea2139..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/debugevent/DebugEventService.java
+++ /dev/null
@@ -1,712 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-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.concurrent.ConcurrentHashMap;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-import net.floodlightcontroller.core.IShutdownListener;
-import net.floodlightcontroller.core.IShutdownService;
-import net.floodlightcontroller.core.module.FloodlightModuleContext;
-import net.floodlightcontroller.core.module.IFloodlightModule;
-import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.debugevent.DebugEventResource.EventInfoResource;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Sets;
-
-/**
- * This class implements a central store for all events used for debugging the
- * system. The basic idea is that given the functionality provided by this
- * class, it should be unnecessary to resort to scraping through system
- * DEBUG/TRACE logs to understand behavior in a running system.
- *
- * @author Saurav
- */
-public class DebugEventService implements IFloodlightModule, IDebugEventService {
-    protected static final Logger log = LoggerFactory.getLogger(DebugEventService.class);
-
-    /**
-     * Every registered event type gets an event id AtomicInt to make the get
-     * operation thread-safe
-     */
-    private final AtomicInteger eventIdCounter = new AtomicInteger();
-
-    /**
-     * Unique Event Instance Id for ack-ing
-     */
-    private final AtomicLong eventInstanceId = new AtomicLong(Long.MAX_VALUE);
-
-    private static final int PCT_LOCAL_CAP = 10; // % of global capacity
-    private static final int MIN_LOCAL_CAPACITY = 10; // elements
-
-    /**
-     * EnumMap from {@link EventFieldType} to {@link CustomFormatter}
-     */
-    static final ImmutableMap<EventFieldType, CustomFormatter<?>> customFormatter =
-            new ImmutableMap.Builder<EventFieldType, CustomFormatter<?>>()
-            .put(EventFieldType.DPID, new CustomFormatterDpid())
-            .put(EventFieldType.IPv4, new CustomFormatterIpv4())
-              .put(EventFieldType.MAC, new CustomFormatterMac())
-              .put(EventFieldType.STRING, new CustomFormatterString())
-              .put(EventFieldType.OBJECT, new CustomFormatterObject())
-              .put(EventFieldType.PRIMITIVE, new CustomFormatterPrimitive())
-              .put(EventFieldType.COLLECTION_IPV4, new CustomFormatterCollectionIpv4())
-              .put(EventFieldType.COLLECTION_ATTACHMENT_POINT, new CustomFormatterCollectionAttachmentPoint())
-              .put(EventFieldType.COLLECTION_OBJECT, new CustomFormatterCollectionObject())
-              .put(EventFieldType.SREF_COLLECTION_OBJECT, new CustomFormatterSrefCollectionObject())
-              .put(EventFieldType.SREF_OBJECT, new CustomFormatterSrefObject())
-              .build();
-
-    /**
-     * Event Information
-     */
-    public static class EventInfo {
-        private final int eventId;
-        private final boolean enabled;
-        private final int bufferCapacity;
-        private int numOfEvents;
-        private final EventType etype;
-        private final String eventDesc;
-        private final String eventName;
-        private final String moduleName;
-        private final String moduleEventName;
-        private final Class<?> eventClass;
-        private final boolean ackable;
-
-        public EventInfo(int eventId, boolean enabled, boolean ackable,
-                         int bufferCapacity, EventType etype,
-                         Class<?> eventClass, String eventDesc,
-                         String eventName, String moduleName) {
-            this.enabled = enabled;
-            this.ackable = ackable;
-            this.eventId = eventId;
-            this.bufferCapacity = bufferCapacity;
-            this.numOfEvents = bufferCapacity;
-            this.etype = etype;
-            this.eventClass = eventClass;
-            this.eventDesc = eventDesc;
-            this.eventName = eventName;
-            this.moduleName = moduleName;
-            this.moduleEventName = moduleName + "/" + eventName;
-        }
-
-        public int getEventId() {
-            return eventId;
-        }
-
-        public boolean isEnabled() {
-            return enabled;
-        }
-
-        public boolean isAckable() {
-            return ackable;
-        }
-
-        public int getBufferCapacity() {
-            return bufferCapacity;
-        }
-
-        public int getNumOfEvents() {
-            return numOfEvents;
-        }
-
-        public EventType getEtype() {
-            return etype;
-        }
-
-        public String getEventDesc() {
-            return eventDesc;
-        }
-
-        public String getEventName() {
-            return eventName;
-        }
-
-        public String getModuleName() {
-            return moduleName;
-        }
-
-        public String getModuleEventName() {
-            return moduleEventName;
-        }
-    }
-
-    // ******************
-    // Global stores
-    // ******************
-
-    /**
-     * Event history for a particular event-id is stored in a circular buffer
-     */
-    protected static class DebugEventHistory {
-        EventInfo einfo;
-        LinkedBlockingDeque<Event> circularEventBuffer;
-
-        public DebugEventHistory(EventInfo einfo, int capacity) {
-            this.einfo = einfo;
-            this.circularEventBuffer = new LinkedBlockingDeque<Event>(
-                                                                      capacity);
-        }
-    }
-
-    /**
-     * Global storage for all event types and their corresponding event buffers.
-     * A particular event type is accessed by directly indexing into the Map
-     * with the corresponding event-id. <br/>
-     * It is a <b>Map</b> with <br/>
-     * <b>Key</b> Integer eventId <br/>
-     * <b>Value</b> DebugEventHistory
-     */
-    protected final ConcurrentHashMap<Integer, DebugEventHistory> allEvents = new ConcurrentHashMap<Integer, DebugEventHistory>();
-
-    /**
-     * Global storage for all event ids registered for a module. The map is
-     * indexed by the module name and event name and returns the event-ids that
-     * correspond to the event types registered by that module (for example
-     * module 'linkdiscovery' may register events that have ids 0 and 1 that
-     * correspond to link up/down events, and receiving malformed LLDP packets,
-     * respectively).
-     */
-    protected final ConcurrentHashMap<String, ConcurrentHashMap<String, Integer>> moduleEvents = new ConcurrentHashMap<String, ConcurrentHashMap<String, Integer>>();
-
-    /**
-     * A collection of event ids that are currently enabled for logging
-     */
-    protected final Set<Integer> currentEvents = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
-
-    // ******************
-    // Thread local stores
-    // ******************
-
-    /**
-     * Thread local storage for events
-     */
-    protected static class LocalEventHistory {
-        private boolean enabled;
-        private final int capacity;
-        private ArrayList<Event> eventList;
-
-        public LocalEventHistory(boolean enabled, int maxCapacity) {
-            this.enabled = enabled;
-            this.eventList = new ArrayList<Event>(maxCapacity);
-            this.capacity = maxCapacity;
-        }
-
-        public boolean add(Event e) {
-            if (this.eventList.size() < capacity) {
-                this.eventList.add(e);
-                return true;
-            }
-            return false;
-        }
-
-        public int drainTo(List<Event> eventList) {
-            int size = this.eventList.size();
-            Iterator<Event> iter = this.eventList.iterator();
-            while (iter.hasNext()) {
-                eventList.add(iter.next());
-            }
-            this.eventList.clear();
-            return size;
-        }
-
-        public boolean isFull() {
-            if (eventList.size() == capacity) return true;
-            return false;
-        }
-
-        public boolean isEmpty() {
-            return this.eventList.isEmpty();
-        }
-    }
-
-    /**
-     * Thread local event buffers used for maintaining event history local to a
-     * thread. Eventually this locally maintained information is flushed into
-     * the global event buffers. <br/>
-     * It is a <b>Map</b> with <br/>
-     * <b>Key</b> Integer eventId <br/>
-     * <b>Value</b> LocalEventHistory
-     */
-    protected final ThreadLocal<Map<Integer, LocalEventHistory>> threadlocalEvents = new ThreadLocal<Map<Integer, LocalEventHistory>>() {
-        @Override
-        protected Map<Integer, LocalEventHistory> initialValue() {
-            return new HashMap<Integer, LocalEventHistory>();
-        }
-    };
-
-    /**
-     * Thread local cache for event-ids that are currently active.
-     */
-    protected final ThreadLocal<Set<Integer>> threadlocalCurrentEvents = new ThreadLocal<Set<Integer>>() {
-        @Override
-        protected Set<Integer> initialValue() {
-            return new HashSet<Integer>();
-        }
-    };
-
-    // *******************************
-    // IEventCategory
-    // *******************************
-
-    protected class EventCategory<T> implements IEventCategory<T> {
-        private final int eventId;
-
-        public EventCategory(int evId) {
-            this.eventId = evId;
-        }
-
-        @Override
-        public void newEventNoFlush(Object event) {
-            if (!validEventId()) return;
-            newEvent(eventId, false, event);
-        }
-
-        @Override
-        public void newEventWithFlush(Object event) {
-            if (!validEventId()) return;
-            newEvent(eventId, true, event);
-        }
-
-        private boolean validEventId() {
-            if (eventId < 0) {
-                throw new IllegalStateException();
-            }
-            return true;
-        }
-    }
-
-    public class EventCategoryBuilder<T> {
-        private int eventId;
-        private String moduleName;
-        private String eventName;
-        private String eventDescription;
-        private EventType eventType;
-        private Class<T> eventClass;
-        private int bufferCapacity;
-        private boolean ackable;
-
-        public EventCategoryBuilder(Class<T> evClass) {
-            this.eventId = eventIdCounter.incrementAndGet();
-            this.eventClass = evClass;
-        }
-
-        public EventCategoryBuilder<T> setModuleName(String moduleName) {
-            this.moduleName = moduleName;
-            return this;
-        }
-
-        public EventCategoryBuilder<T> setEventName(String eventName) {
-            this.eventName = eventName;
-            return this;
-        }
-
-        public EventCategoryBuilder<T> setEventDescription(String eventDescription) {
-            this.eventDescription = eventDescription;
-            return this;
-        }
-
-        public EventCategoryBuilder<T> setEventType(EventType et) {
-            this.eventType = et;
-            return this;
-        }
-
-        public EventCategoryBuilder<T> setBufferCapacity(int bufferCapacity) {
-            this.bufferCapacity = bufferCapacity;
-            return this;
-        }
-
-        public EventCategoryBuilder<T> setAckable(boolean ackable) {
-            this.ackable = ackable;
-            return this;
-        }
-
-        /**
-         * Build a new {@link EventCategory<T>} <br/>
-         * Requires the following parameters to be set before being called:
-         *
-         * @param moduleName
-         *            module registering event eg. linkdiscovery, virtualrouting.
-         * @param eventName
-         *            name given to event.
-         * @param eventDescription
-         *            A descriptive string describing the event.
-         * @param eventType
-         *            EventType for this event. On-demand events have to be
-         *            explicitly enabled using other methods in this API
-         * @param eventClass
-         *            A user defined class that annotates the fields with
-         *            @EventColumn. This class specifies the fields/columns for this
-         *            event.
-         * @param bufferCapacity
-         *            Number of events to store for this event in a circular buffer.
-         *            Older events will be discarded once the buffer is full.
-         * @param ackable
-         *            is the event used as part of ackable-event framework boolean
-         *
-         * @return IEventCategory with <b>newEvent</b> method that can be used
-         * to create instances of event of the given eventClass
-         */
-        public EventCategory<T> register() {
-            // register event id for moduleName
-            moduleEvents.putIfAbsent(moduleName,
-                                     new ConcurrentHashMap<String, Integer>());
-            Integer eventExists = moduleEvents.get(moduleName)
-                                              .putIfAbsent(eventName, eventId);
-            if (eventExists != null) {
-                log.error("Duplicate event registration for moduleName {} eventName {}",
-                          moduleName, eventName);
-                return new EventCategory<T>(eventExists);
-            }
-
-            // create storage for event-type
-            boolean enabled = (eventType == EventType.ALWAYS_LOG) ? true : false;
-            EventInfo ei = new EventInfo(eventId, enabled, ackable,
-                                         bufferCapacity, eventType, eventClass,
-                                         eventDescription, eventName, moduleName);
-            allEvents.put(eventId, new DebugEventHistory(ei, bufferCapacity));
-            if (enabled) {
-                currentEvents.add(eventId);
-            }
-
-            return new EventCategory<T>(this.eventId);
-        }
-    }
-
-    // *******************************
-    // IDebugEventService
-    // *******************************
-
-    @Override
-    public <T> EventCategoryBuilder<T> buildEvent(Class<T> evClass) {
-        return new EventCategoryBuilder<T>(evClass);
-    }
-
-    private void flushLocalToGlobal(int eventId, LocalEventHistory le) {
-        DebugEventHistory de = allEvents.get(eventId);
-        if (de.einfo.enabled) {
-            List<Event> transferEvents = new ArrayList<Event>();
-            // drain local buffer to Collection
-            int size = le.drainTo(transferEvents);
-            // if global buffer doesn't have enough space, clear
-            // some space
-            int requiredSpace = size
-                                - de.circularEventBuffer.remainingCapacity();
-            if (requiredSpace > 0) {
-                for (int i = 0; i < requiredSpace; i++) {
-                    de.circularEventBuffer.removeFirst();
-                }
-            }
-            de.circularEventBuffer.addAll(transferEvents);
-        } else {
-            le.enabled = false;
-            this.threadlocalCurrentEvents.get().remove(eventId);
-        }
-    }
-
-    private void newEvent(int eventId, boolean flushNow, Object eventData) {
-        if (eventId < 0) {
-            throw new IllegalStateException("Invalid eventId");
-        }
-
-        Map<Integer, LocalEventHistory> thishist = this.threadlocalEvents.get();
-        if (!thishist.containsKey(eventId)) {
-            // seeing this event for the first time in this thread - create
-            // local
-            // store by consulting global store
-            if (allEvents.containsKey(eventId)) {
-                DebugEventHistory de = allEvents.get(eventId);
-                boolean enabled = de.einfo.enabled;
-                int localCapacity = de.einfo.bufferCapacity * PCT_LOCAL_CAP
-                                    / 100;
-                if (localCapacity < 10) localCapacity = MIN_LOCAL_CAPACITY;
-                thishist.put(eventId, new LocalEventHistory(enabled,
-                                                            localCapacity));
-                if (enabled) {
-                    Set<Integer> thisset = this.threadlocalCurrentEvents.get();
-                    thisset.add(eventId);
-                }
-            } else {
-                log.error("updateEvent seen locally for event {} but no global"
-                                  + "storage exists for it yet .. not updating",
-                          eventId);
-                return;
-            }
-        }
-
-        // update local store if enabled locally for updating
-        LocalEventHistory le = thishist.get(eventId);
-        if (le.enabled) {
-            try {
-                le.add(new Event(System.currentTimeMillis(),
-                                 Thread.currentThread().getId(),
-                                 Thread.currentThread().getName(),
-                                 eventData,
-                                 /*
-                                  * the eventInstanceId is started in reverse
-                                  * order so BigDB gets the values in ascending
-                                  * order Initialization in startUp()
-                                  */
-                                 eventInstanceId.decrementAndGet()));
-
-                if (le.isFull() || flushNow) {
-                    flushLocalToGlobal(eventId, le);
-                }
-            } catch (IllegalStateException ise) {
-                log.debug("Exception while adding event locally: "
-                          + ise.getMessage());
-            }
-        }
-    }
-
-    @Override
-    public void flushEvents() {
-        Map<Integer, LocalEventHistory> thishist = this.threadlocalEvents.get();
-        Set<Integer> thisset = this.threadlocalCurrentEvents.get();
-
-        for (int eventId : thisset) {
-            if (thishist.containsKey(eventId)) {
-                LocalEventHistory le = thishist.get(eventId);
-                if (!le.isEmpty()) {
-                    flushLocalToGlobal(eventId, le);
-                }
-            }
-        }
-
-        // sync thread local currently enabled set of eventIds with global set.
-        Sets.SetView<Integer> sv = Sets.difference(currentEvents, thisset);
-        for (int eventId : sv) {
-            if (thishist.containsKey(eventId)) {
-                thishist.get(eventId).enabled = true;
-                thisset.add(eventId);
-            }
-        }
-
-    }
-
-    @Override
-    public boolean containsModuleEventName(String moduleName,
-                                           String eventName) {
-        if (!moduleEvents.containsKey(moduleName)) return false;
-        if (moduleEvents.get(moduleName).containsKey(eventName))
-                                                                return true;
-        return false;
-    }
-
-    @Override
-    public boolean containsModuleName(String moduleName) {
-        return moduleEvents.containsKey(moduleName);
-    }
-
-    @Override
-    public List<EventInfoResource> getAllEventHistory() {
-        List<EventInfoResource> moduleEventList = new ArrayList<EventInfoResource>();
-        for (Map<String, Integer> modev : moduleEvents.values()) {
-            for (int eventId : modev.values()) {
-                if (allEvents.containsKey(eventId)) {
-                    DebugEventHistory de = allEvents.get(eventId);
-
-                    List<EventResource> eventData = new ArrayList<EventResource>();
-                    // take snapshot and iterate
-                    Iterator<Event> iter = de.circularEventBuffer.descendingIterator();
-                    while (iter.hasNext()) {
-                        Event e = iter.next();
-                        eventData.add(e.getFormattedEvent(de.einfo.eventClass,
-                                                          de.einfo.moduleEventName));
-                    }
-                    moduleEventList.add(new EventInfoResource(de.einfo,
-                                                              eventData));
-                }
-            }
-        }
-        traceLogDebugHistory(moduleEventList);
-        return moduleEventList;
-    }
-
-    @Override
-    public List<EventInfoResource> getModuleEventHistory(String moduleName) {
-        if (!moduleEvents.containsKey(moduleName))
-                                                  return Collections.emptyList();
-        List<EventInfoResource> moduleEventList = new ArrayList<EventInfoResource>();
-        for (int eventId : moduleEvents.get(moduleName).values()) {
-            if (allEvents.containsKey(eventId)) {
-                DebugEventHistory de = allEvents.get(eventId);
-
-                List<EventResource> eventData = new ArrayList<EventResource>();
-                // take snapshot and iterate
-                Iterator<Event> iter = de.circularEventBuffer.descendingIterator();
-                while (iter.hasNext()) {
-                    Event e = iter.next();
-                    eventData.add(e.getFormattedEvent(de.einfo.eventClass,
-                                                      de.einfo.moduleEventName));
-                }
-                moduleEventList.add(new EventInfoResource(de.einfo,
-                                                          eventData));
-            }
-        }
-        traceLogDebugHistory(moduleEventList);
-        return moduleEventList;
-    }
-
-    @Override
-    public EventInfoResource getSingleEventHistory(String moduleName,
-                                                   String eventName,
-                                                   int numOfEvents) {
-        if (!moduleEvents.containsKey(moduleName)) return null;
-        Integer eventId = moduleEvents.get(moduleName).get(eventName);
-        if (eventId == null) return null;
-
-        if (!allEvents.containsKey(eventId)) return null;
-
-        DebugEventHistory de = allEvents.get(eventId);
-        if (numOfEvents == 0) numOfEvents = de.einfo.bufferCapacity;
-
-        de.einfo.numOfEvents = numOfEvents;
-        int num = 1;
-        List<EventResource> eventData = new ArrayList<EventResource>();
-        // take snapshot and iterate
-        Iterator<Event> iter = de.circularEventBuffer.descendingIterator();
-        while (iter.hasNext()) {
-            Event e = iter.next();
-            if (num > numOfEvents) break;
-            eventData.add(e.getFormattedEvent(de.einfo.eventClass,
-                                              de.einfo.moduleEventName));
-            num++;
-        }
-        EventInfoResource ret = new EventInfoResource(de.einfo, eventData);
-        traceLogDebugHistory(Collections.singletonList(ret));
-        return ret;
-    }
-
-    @Override
-    public void resetAllEvents() {
-        for (Map<String, Integer> eventMap : moduleEvents.values()) {
-            for (Integer evId : eventMap.values()) {
-                allEvents.get(evId).circularEventBuffer.clear();
-            }
-        }
-    }
-
-    @Override
-    public void resetAllModuleEvents(String moduleName) {
-        if (!moduleEvents.containsKey(moduleName)) return;
-        Map<String, Integer> modEvents = moduleEvents.get(moduleName);
-        for (Integer evId : modEvents.values()) {
-            allEvents.get(evId).circularEventBuffer.clear();
-        }
-    }
-
-    @Override
-    public void resetSingleEvent(String moduleName, String eventName) {
-        if (!moduleEvents.containsKey(moduleName)) return;
-        Integer eventId = moduleEvents.get(moduleName).get(eventName);
-        if (eventId == null) return;
-
-        if (allEvents.containsKey(eventId)) {
-            allEvents.get(eventId).circularEventBuffer.clear();
-        }
-    }
-
-    @Override
-    public void setAck(int eventId, long eventInstanceId, boolean ack) {
-        if (allEvents.containsKey(eventId)) {
-            for (Event e : allEvents.get(eventId).circularEventBuffer) {
-                if (e.getEventInstanceId() == eventInstanceId) {
-                    e.setAcked(ack);
-                }
-            }
-        }
-    }
-
-    @Override
-    public List<String> getModuleList() {
-        List<String> el = new ArrayList<String>();
-        el.addAll(moduleEvents.keySet());
-        return el;
-    }
-
-    @Override
-    public List<String> getModuleEventList(String moduleName) {
-        if (!moduleEvents.containsKey(moduleName))
-                                                  return Collections.emptyList();
-        List<String> el = new ArrayList<String>();
-        el.addAll(moduleEvents.get(moduleName).keySet());
-        return el;
-    }
-
-
-    private void traceLogDebugHistory(List<EventInfoResource> l) {
-        if (!log.isTraceEnabled()) {
-            return;
-        }
-        for (EventInfoResource eir: l) {
-            for (EventResource der: eir.getEvents()) {
-                log.trace("{}", der);
-            }
-        }
-    }
-
-    private class ShutdownListenenerDelegate implements IShutdownListener {
-        @Override
-        public void floodlightIsShuttingDown() {
-            for (EventInfoResource eir: getAllEventHistory()) {
-                for (EventResource der: eir.getEvents()) {
-                    log.info("{}", der);
-                }
-            }
-        }
-    }
-
-    // *******************************
-    // IFloodlightModule
-    // *******************************
-
-    @Override
-    public Collection<Class<? extends IFloodlightService>>
-            getModuleServices() {
-        Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
-        l.add(IDebugEventService.class);
-        return l;
-    }
-
-    @Override
-    public Map<Class<? extends IFloodlightService>, IFloodlightService>
-            getServiceImpls() {
-        Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
-        m.put(IDebugEventService.class, this);
-        return m;
-    }
-
-    @Override
-    public Collection<Class<? extends IFloodlightService>>
-            getModuleDependencies() {
-        ArrayList<Class<? extends IFloodlightService>> deps = new ArrayList<Class<? extends IFloodlightService>>();
-        deps.add(IShutdownService.class);
-        return deps;
-    }
-
-    @Override
-    public void init(FloodlightModuleContext context) {
-    }
-
-    @Override
-    public void startUp(FloodlightModuleContext context) {
-        IShutdownService shutdownService =
-                context.getServiceImpl(IShutdownService.class);
-        shutdownService.registerShutdownListener(new ShutdownListenenerDelegate());
-        DebugEventAppender.setDebugEventServiceImpl(this);
-    }
-}
diff --git a/src/main/java/net/floodlightcontroller/debugevent/Event.java b/src/main/java/net/floodlightcontroller/debugevent/Event.java
deleted file mode 100644
index d3a0cc2d4041e12a45025f4f503041aa4ef9d258..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/debugevent/Event.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import java.lang.reflect.Field;
-
-import net.floodlightcontroller.debugevent.DebugEventService.EventCategory;
-import net.floodlightcontroller.debugevent.EventResource.EventResourceBuilder;
-import net.floodlightcontroller.debugevent.EventResource.Metadata;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
-
-/**
- * Generic Event class used to store different categories of events.
- * DebugEventService uses this class to store each Event instance.
- * Each {@link EventCategory} has a different object of type <b>EventClass</b>.
- * This is used internally for all DebugEvent processing. For display via REST
- * and CLI, it is transformed to {@link EventResource} object.
- */
-public class Event {
-    private final long eventInstanceId;
-    private volatile boolean acked;
-    private final long timeMs;
-    private final long threadId;
-    private final String threadName;
-    private final Object eventData;
-
-    public Event(long timeMs, long threadId, String threadName,
-                 Object eventData, long eventInstanceId) {
-        this.timeMs = timeMs;
-        this.threadId = threadId;
-        this.threadName = threadName;
-        this.eventData = eventData;
-        this.eventInstanceId = eventInstanceId;
-        this.acked = false;
-    }
-
-    public long getTimeMs() {
-        return timeMs;
-    }
-
-    public long getThreadId() {
-        return threadId;
-    }
-
-    public String getThreadName() {
-        return threadName;
-    }
-
-    public Object geteventData() {
-        return eventData;
-    }
-
-    public long getEventInstanceId() {
-        return eventInstanceId;
-    }
-
-    public boolean isAcked() {
-        return acked;
-    }
-
-    public void setAcked(boolean acked) {
-        this.acked = acked;
-    }
-
-    @Override
-    public String toString() {
-        return "Event [eventInstanceId=" + eventInstanceId + ", acked="
-               + acked + ", timeMs=" + timeMs + ", threadId=" + threadId
-               + ", threadName=" + threadName + ", eventData=" + eventData
-               + "]";
-    }
-
-    public EventResource getFormattedEvent(Class<?> eventClass,
-                                           String moduleEventName) {
-        if (eventClass == null || !eventClass.equals(eventData.getClass())) {
-            EventResourceBuilder edb = new EventResourceBuilder();
-            edb.dataFields.add(new Metadata("Error",
-                                            "null event data or event-class does not match event-data"));
-            return edb.build();
-        }
-
-        EventResourceBuilder edb = new EventResourceBuilder();
-        edb.setTimeStamp(timeMs);
-        edb.setThreadId(threadId);
-        edb.setThreadName(threadName);
-        edb.setModuleEventName(moduleEventName);
-        edb.setEventInstanceId(eventInstanceId);
-        edb.setAcked(acked);
-        customFormat(eventClass, eventData, edb);
-        return edb.build();
-    }
-
-    @SuppressWarnings("unchecked")
-    private void customFormat(Class<?> clazz, Object eventData,
-                              EventResourceBuilder eventDataBuilder) {
-        for (Field f : clazz.getDeclaredFields()) {
-            EventColumn ec = f.getAnnotation(EventColumn.class);
-            if (ec == null) continue;
-            f.setAccessible(true);
-            try {
-                Object obj = f.get(eventData);
-                @SuppressWarnings("rawtypes")
-                CustomFormatter cf = DebugEventService.customFormatter.get(ec.description());
-
-                if (cf == null) {
-                    throw new IllegalArgumentException(
-                                                       "CustomFormatter for "
-                                                               + ec.description()
-                                                               + " does not exist.");
-                } else {
-                    cf.customFormat(obj, ec.name(), eventDataBuilder);
-                }
-            } catch (ClassCastException e) {
-                eventDataBuilder.dataFields.add(new Metadata("Error",
-                                                             e.getMessage()));
-            } catch (IllegalArgumentException e) {
-                eventDataBuilder.dataFields.add(new Metadata("Error",
-                                                             e.getMessage()));
-            } catch (IllegalAccessException e) {
-                eventDataBuilder.dataFields.add(new Metadata("Error",
-                                                             e.getMessage()));
-            }
-        }
-    }
-
-}
diff --git a/src/main/java/net/floodlightcontroller/debugevent/EventResource.java b/src/main/java/net/floodlightcontroller/debugevent/EventResource.java
deleted file mode 100644
index 6ce52b53bf55ff15c054412cc808479a857a4a29..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/debugevent/EventResource.java
+++ /dev/null
@@ -1,258 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.annotation.concurrent.Immutable;
-
-import java.util.Date;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * Event Instance of a particular event category. This is only for REST purpose.
- * Actual {@link Event} class consists of Object of Event. Here we parse it into
- * its string form using {@link getCustomFormatter} and return for REST and CLI.
- */
-@Immutable
-public class EventResource {
-    private final Date timestamp;
-    private final long threadId;
-    private final String threadName;
-    private final String moduleEventName;
-    private final ImmutableList<Metadata> dataFields;
-    // NOTE: this is only for CLI purpose. cascaded fields show up as
-    // different tables in CLI which is not very useful
-    private final String dataString;
-    long eventInstanceId;
-    boolean acked;
-
-    private EventResource(EventResourceBuilder evInstanceBuilder) {
-        this.timestamp = evInstanceBuilder.timestamp;
-        this.threadId = evInstanceBuilder.threadId;
-        this.threadName = evInstanceBuilder.threadName;
-        this.moduleEventName = evInstanceBuilder.moduleEventName;
-        this.dataFields = ImmutableList.copyOf(evInstanceBuilder.dataFields);
-        this.dataString = this.dataFields.toString();
-        this.eventInstanceId = evInstanceBuilder.eventInstanceId;
-        this.acked = evInstanceBuilder.acked;
-    }
-
-    public Date getTimestamp() {
-        return timestamp;
-    }
-
-    public String getModuleEventName() {
-        return moduleEventName;
-    }
-
-    public List<Metadata> getDataFields() {
-        return dataFields;
-    }
-
-    public String getDataString() {
-        return dataString;
-    }
-
-    public long getEventInstanceId() {
-        return eventInstanceId;
-    }
-
-    public boolean isAcked() {
-        return acked;
-    }
-
-    public static class EventResourceBuilder {
-        private Date timestamp;
-        private long threadId;
-        private String threadName;
-        private String moduleEventName;
-        protected List<Metadata> dataFields;
-        long eventInstanceId;
-        boolean acked;
-
-        public EventResourceBuilder() {
-            this.dataFields = new ArrayList<Metadata>();
-        }
-
-        public EventResource build() {
-            return new EventResource(this);
-        }
-
-        public Date getTimestamp() {
-            return timestamp;
-        }
-
-        public void setTimeStamp(long timeMs) {
-            this.timestamp = new Date(timeMs);
-        }
-
-        public long getThreadId() {
-            return threadId;
-        }
-
-        public void setThreadId(long threadId) {
-            this.threadId = threadId;
-        }
-
-        public String getThreadName() {
-            return threadName;
-        }
-
-        public void setThreadName(String threadName) {
-            this.threadName = threadName;
-        }
-
-        public String getModuleEventName() {
-            return moduleEventName;
-        }
-
-        public void setModuleEventName(String moduleEventName) {
-            this.moduleEventName = moduleEventName;
-        }
-
-        public List<Metadata> getDataFields() {
-            return dataFields;
-        }
-
-        public void setDataFields(List<Metadata> dataFields) {
-            this.dataFields = dataFields;
-        }
-
-        public long getEventInstanceId() {
-            return eventInstanceId;
-        }
-
-        public void setEventInstanceId(long eventInstanceId) {
-            this.eventInstanceId = eventInstanceId;
-        }
-
-        public boolean isAcked() {
-            return acked;
-        }
-
-        public void setAcked(boolean acked) {
-            this.acked = acked;
-        }
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result
-                 + ((dataFields == null) ? 0 : dataFields.hashCode());
-        result = prime * result
-                 + ((dataString == null) ? 0 : dataString.hashCode());
-        result = prime
-                 * result
-                 + ((moduleEventName == null) ? 0
-                                             : moduleEventName.hashCode());
-        result = prime * result + (int) (threadId ^ (threadId >>> 32));
-        result = prime * result
-                 + ((threadName == null) ? 0 : threadName.hashCode());
-        result = prime * result
-                 + ((timestamp == null) ? 0 : timestamp.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        EventResource other = (EventResource) obj;
-        if (dataFields == null) {
-            if (other.dataFields != null) return false;
-        } else if (!dataFields.equals(other.dataFields)) return false;
-        if (dataString == null) {
-            if (other.dataString != null) return false;
-        } else if (!dataString.equals(other.dataString)) return false;
-        if (moduleEventName == null) {
-            if (other.moduleEventName != null) return false;
-        } else if (!moduleEventName.equals(other.moduleEventName))
-                                                                  return false;
-        if (threadId != other.threadId) return false;
-        if (threadName == null) {
-            if (other.threadName != null) return false;
-        } else if (!threadName.equals(other.threadName)) return false;
-        if (timestamp == null) {
-            if (other.timestamp != null) return false;
-        } else if (!timestamp.equals(other.timestamp)) return false;
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append(moduleEventName);
-        builder.append(" ");
-        builder.append(timestamp);
-        builder.append(", threadId=");
-        builder.append(threadId);
-        builder.append(", threadName=\"");
-        builder.append(threadName);
-        builder.append("\", dataFields=");
-        builder.append(dataFields);
-        builder.append(", eventInstanceId=");
-        builder.append(eventInstanceId);
-        builder.append(", acked=");
-        builder.append(acked);
-        return builder.toString();
-    }
-
-    /**
-     * Actual {@link Event} has Object of Event. The fields of that Object are
-     * converted to {@literal String} and returned as {@link Metadata} with
-     * {@link EventResource}
-     */
-    @Immutable
-    public static class Metadata {
-        private final String eventClass;
-        private final String eventData;
-
-        public Metadata(String eventClass, String eventData) {
-            this.eventClass = eventClass;
-            this.eventData = eventData;
-        }
-
-        public String getEventClass() {
-            return eventClass;
-        }
-
-        public String getEventData() {
-            return eventData;
-        }
-
-        @Override
-        public String toString() {
-            return this.eventClass + ":" + this.eventData;
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result
-                     + ((eventClass == null) ? 0 : eventClass.hashCode());
-            result = prime * result
-                     + ((eventData == null) ? 0 : eventData.hashCode());
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) return true;
-            if (obj == null) return false;
-            if (getClass() != obj.getClass()) return false;
-            Metadata other = (Metadata) obj;
-            if (eventClass == null) {
-                if (other.eventClass != null) return false;
-            } else if (!eventClass.equals(other.eventClass)) return false;
-            if (eventData == null) {
-                if (other.eventData != null) return false;
-            } else if (!eventData.equals(other.eventData)) return false;
-            return true;
-        }
-    }
-}
diff --git a/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java b/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java
deleted file mode 100644
index 9016edab905e493f4f5e64b0e041b3adcf56c53e..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java
+++ /dev/null
@@ -1,193 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.List;
-
-import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.debugevent.DebugEventResource.EventInfoResource;
-import net.floodlightcontroller.debugevent.DebugEventService.EventCategory;
-import net.floodlightcontroller.debugevent.DebugEventService.EventCategoryBuilder;
-
-public interface IDebugEventService extends IFloodlightService {
-
-    /**
-     * Different event types. Events that are meant to be logged on demand need
-     * to be separately enabled/disabled.
-     */
-    public enum EventType {
-        ALWAYS_LOG, LOG_ON_DEMAND
-    }
-
-    /**
-     * Is the Event <b>ACKABLE</b> or <b>NOT_ACKABLE</b>
-     */
-    public enum AckableEvent {
-        ACKABLE, NOT_ACKABLE
-    }
-
-    /**
-     * Describes the type of field obtained from reflection
-     */
-    enum EventFieldType {
-        DPID, IPv4, IPv6, MAC, STRING, OBJECT, PRIMITIVE, COLLECTION_IPV4,
-        COLLECTION_ATTACHMENT_POINT, COLLECTION_OBJECT, SREF_COLLECTION_OBJECT,
-        SREF_OBJECT
-    }
-
-    /**
-     * EventColumn is the only annotation given to the fields of the event when
-     * updating an event.
-     */
-    @Target(ElementType.FIELD)
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface EventColumn {
-        String name() default "param";
-
-        EventFieldType description() default EventFieldType.PRIMITIVE;
-    }
-
-    /**
-     * Debug Event Qualifiers
-     */
-    public static final String EV_MDATA_WARN = "warn";
-    public static final String EV_MDATA_ERROR = "error";
-
-    /**
-     * Returns an {@link EventCategoryBuilder} that can be used to build a new
-     * {@link EventCategory}. Before calling the {@literal build} method, set
-     * the following parameters:
-     *
-     * @param moduleName
-     *            module registering event eg. linkdiscovery, virtualrouting.
-     * @param eventName
-     *            name given to event.
-     * @param eventDescription
-     *            A descriptive string describing the event.
-     * @param eventType
-     *            EventType for this event. On-demand events have to be
-     *            explicitly enabled using other methods in this API
-     * @param eventClass
-     *            A user defined class that annotates the fields with
-     *            @EventColumn. This class specifies the fields/columns for this
-     *            event.
-     * @param bufferCapacity
-     *            Number of events to store for this event in a circular buffer.
-     *            Older events will be discarded once the buffer is full.
-     * @param ackable
-     *            is the event used as part of ackable-event framework boolean
-     *
-     * @return IEventCategory with <b>newEvent</b> method that can be used
-     * to create instances of event of the given eventClass
-     * @throws MaxEventsRegistered
-     */
-    public <T> EventCategoryBuilder<T> buildEvent(Class<T> evClass);
-
-    /**
-     * Update the global event stores with values from the thread local stores.
-     * This method is not typically intended for use by any module. It's typical
-     * usage is from floodlight core for events that happen in the packet
-     * processing pipeline. For other rare events, flushEvents should be called.
-     */
-    public void flushEvents();
-
-    /**
-     * Determine if eventName is a registered event for a given moduleName
-     */
-    public boolean containsModuleEventName(String moduleName,
-                                           String eventName);
-
-    /**
-     * Determine if any events have been registered for module of name
-     * moduleName
-     */
-    public boolean containsModuleName(String moduleName);
-
-    /**
-     * Get event history for all events. This call can be expensive as it
-     * formats the event histories for all events.
-     *
-     * @return a list of all event histories or an empty list if no events have
-     *         been registered
-     */
-    public List<EventInfoResource> getAllEventHistory();
-
-    /**
-     * Get event history for all events registered for a given moduleName
-     *
-     * @return a list of all event histories for all events registered for the
-     *         the module or an empty list if there are no events for this
-     *         module
-     */
-    public List<EventInfoResource> getModuleEventHistory(String moduleName);
-
-    /**
-     * Get event history for a single event
-     *
-     * @param moduleName
-     *            registered module name
-     * @param eventName
-     *            registered event name for moduleName
-     * @param numOfEvents
-     *            last X events
-     * @return DebugEventInfo for that event, or null if the moduleEventName
-     *         does not correspond to a registered event.
-     */
-    public EventInfoResource getSingleEventHistory(String moduleName,
-                                                   String eventName,
-                                                   int numOfEvents);
-
-    /**
-     * Wipe out all event history for all registered events
-     */
-    public void resetAllEvents();
-
-    /**
-     * Wipe out all event history for all events registered for a specific
-     * module
-     *
-     * @param moduleName
-     *            registered module name
-     */
-    public void resetAllModuleEvents(String moduleName);
-
-    /**
-     * Wipe out event history for a single event
-     *
-     * @param moduleName
-     *            registered module name
-     * @param eventName
-     *            registered event name for moduleName
-     */
-    public void resetSingleEvent(String moduleName, String eventName);
-
-    /**
-     * Retrieve a list of moduleNames registered for debug events or an empty
-     * list if no events have been registered in the system
-     */
-    public List<String> getModuleList();
-
-    /**
-     * Returns a list of all events registered for a specific moduleName or a
-     * empty list
-     */
-    public List<String> getModuleEventList(String moduleName);
-
-    /**
-     * Sets the 'ack' for the individual {@link DebugEventService} instance
-     * pointed by the <b>eventId</b> and <b>eventInstanceId</b> given in the
-     * PATCH request <br/>
-     * Returns void
-     *
-     * @param eventId
-     *            unique event queue identifier
-     * @param eventInstanceId
-     *            unique event identifier
-     * @param ack
-     *            boolean ack - true or false
-     */
-    public void setAck(int eventId, long eventInstanceId, boolean ack);
-
-}
diff --git a/src/main/java/net/floodlightcontroller/debugevent/IEventCategory.java b/src/main/java/net/floodlightcontroller/debugevent/IEventCategory.java
deleted file mode 100644
index ebd17e45e821cd07214bd838de824bac5d168f3a..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/debugevent/IEventCategory.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-/**
- * EventCategory is used to log events for pre-registered events.
- */
-public interface IEventCategory<T> {
-
-    /**
-     * Logs the instance of the event thread-locally. Flushing to the global
-     * circular buffer for this event is delayed resulting in better
-     * performance. This method should typically be used by those events that
-     * happen in the packet processing pipeline
-     *
-     * @param event
-     *            an instance of the user-defined event of type T
-     */
-    public void newEventNoFlush(T event);
-
-    /**
-     * Logs the instance of the event thread-locally and immediately flushes to
-     * the global circular buffer for this event. This method should typically
-     * be used by those events that happen outside the packet processing
-     * pipeline
-     *
-     * @param event
-     *            an instance of the user-defined event of type T
-     */
-    public void newEventWithFlush(T event);
-
-}
diff --git a/src/main/java/net/floodlightcontroller/debugevent/MockDebugEventService.java b/src/main/java/net/floodlightcontroller/debugevent/MockDebugEventService.java
deleted file mode 100644
index cf43d81469f2369c48f68cae97fa9a5d3b37339d..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/debugevent/MockDebugEventService.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import net.floodlightcontroller.core.module.FloodlightModuleContext;
-import net.floodlightcontroller.core.module.FloodlightModuleException;
-import net.floodlightcontroller.core.module.IFloodlightModule;
-import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.debugevent.DebugEventResource.EventInfoResource;
-import net.floodlightcontroller.debugevent.DebugEventService.EventCategoryBuilder;
-
-public class MockDebugEventService implements IFloodlightModule, IDebugEventService {
-
-    @Override
-    public <T> EventCategoryBuilder<T> buildEvent(Class<T> evClass) {
-        DebugEventService des = new DebugEventService();
-        return des.buildEvent(evClass);
-    }
-
-    @Override
-    public void flushEvents() {
-
-    }
-
-    @Override
-    public boolean containsModuleEventName(String moduleName,
-                                           String eventName) {
-        return false;
-    }
-
-    @Override
-    public boolean containsModuleName(String moduleName) {
-        return false;
-    }
-
-    @Override
-    public List<EventInfoResource> getAllEventHistory() {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public List<EventInfoResource> getModuleEventHistory(String moduleName) {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public EventInfoResource getSingleEventHistory(String moduleName,
-                                                   String eventName,
-                                                   int numOfEvents) {
-        return null;
-    }
-
-    @Override
-    public void resetAllEvents() {
-
-    }
-
-    @Override
-    public void resetAllModuleEvents(String moduleName) {
-
-    }
-
-    @Override
-    public void resetSingleEvent(String moduleName, String eventName) {
-
-    }
-
-    @Override
-    public List<String> getModuleList() {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public List<String> getModuleEventList(String moduleName) {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public void setAck(int eventId, long eventInstanceId, boolean ack) {
-
-    }
-
-    @Override
-    public Collection<Class<? extends IFloodlightService>>
-            getModuleServices() {
-        Collection<Class<? extends IFloodlightService>> services =
-                new ArrayList<Class<? extends IFloodlightService>>(1);
-        services.add(IDebugEventService.class);
-        return services;
-    }
-
-    @Override
-    public Map<Class<? extends IFloodlightService>, IFloodlightService>
-            getServiceImpls() {
-        Map<Class<? extends IFloodlightService>, IFloodlightService> m =
-                new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
-        m.put(IDebugEventService.class, this);
-        return m;
-    }
-
-    @Override
-    public Collection<Class<? extends IFloodlightService>>
-            getModuleDependencies() {
-        return null;
-    }
-
-    @Override
-    public
-            void
-            init(FloodlightModuleContext context)
-                                                 throws FloodlightModuleException {
-
-    }
-
-    @Override
-    public
-            void
-            startUp(FloodlightModuleContext context)
-                                                    throws FloodlightModuleException {
-
-    }
-}
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java b/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java
index abede26af415d1854dd080bc9131300ba45c142e..0e3ea724de50c13446107ac77eee3d493d7d7228 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/SwitchPort.java
@@ -20,8 +20,7 @@ package net.floodlightcontroller.devicemanager;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFPort;
 
-import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
-import net.floodlightcontroller.core.web.serializers.OFPortSerializer;
+import net.floodlightcontroller.core.types.NodePortTuple;
 
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
@@ -32,7 +31,7 @@ import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
  * @author readams
  *
  */
-public class SwitchPort {
+public class SwitchPort extends NodePortTuple {
     @JsonSerialize(using=ToStringSerializer.class)
     public enum ErrorStatus {
         DUPLICATE_DEVICE("duplicate-device");
@@ -57,8 +56,6 @@ public class SwitchPort {
         }
     }
 
-    private final DatapathId switchDPID;
-    private final OFPort port;
     private final ErrorStatus errorStatus;
 
     /**
@@ -68,9 +65,7 @@ public class SwitchPort {
      * @param errorStatus any error status for the switch port
      */
     public SwitchPort(DatapathId switchDPID, OFPort port, ErrorStatus errorStatus) {
-        super();
-        this.switchDPID = switchDPID;
-        this.port = port;
+        super(switchDPID, port);
         this.errorStatus = errorStatus;
     }
 
@@ -80,9 +75,7 @@ public class SwitchPort {
      * @param port the port
      */
     public SwitchPort(DatapathId switchDPID, OFPort port) {
-        super();
-        this.switchDPID = switchDPID;
-        this.port = port;
+        super(switchDPID, port);
         this.errorStatus = null;
     }
 
@@ -90,16 +83,6 @@ public class SwitchPort {
     // Getters/Setters
     // ***************
 
-    @JsonSerialize(using=DPIDSerializer.class)
-    public DatapathId getSwitchDPID() {
-        return switchDPID;
-    }
-
-    @JsonSerialize(using=OFPortSerializer.class)
-    public OFPort getPort() {
-        return port;
-    }
-
     public ErrorStatus getErrorStatus() {
         return errorStatus;
     }
@@ -108,35 +91,31 @@ public class SwitchPort {
     // Object
     // ******
 
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result
-                        + ((errorStatus == null)
-                                ? 0
-                                : errorStatus.hashCode());
-        result = prime * result + port.getPortNumber();
-        result = prime * result + (int) (switchDPID.getLong() ^ (switchDPID.getLong() >>> 32));
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        SwitchPort other = (SwitchPort) obj;
-        if (errorStatus != other.errorStatus) return false;
-        if (!port.equals(other.port)) return false;
-        if (!switchDPID.equals(other.switchDPID)) return false;
-        return true;
-    }
-
     @Override
     public String toString() {
-        return "SwitchPort [switchDPID=" + switchDPID.toString() +
-               ", port=" + port + ", errorStatus=" + errorStatus + "]";
+        return "SwitchPort [switchDPID=" + getNodeId().toString() +
+               ", port=" + getPortId() + ", errorStatus=" + errorStatus + "]";
     }
 
-}
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = super.hashCode();
+		result = prime * result + ((errorStatus == null) ? 0 : errorStatus.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (!super.equals(obj))
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		SwitchPort other = (SwitchPort) obj;
+		if (errorStatus != other.errorStatus)
+			return false;
+		return true;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
index 5dced9cb19e49f3847ff2fd90ebb768ae8044f55..3fe2b492e86cbed718d2089055ab15b60758f114 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
@@ -791,8 +791,8 @@ public class Device implements IDevice {
 	public VlanVid[] getSwitchPortVlanIds(SwitchPort swp) {
 		TreeSet<VlanVid> vals = new TreeSet<VlanVid>();
 		for (Entity e : entities) {
-			if (e.switchDPID.equals(swp.getSwitchDPID())
-					&& e.switchPort.equals(swp.getPort())) {
+			if (e.switchDPID.equals(swp.getNodeId())
+					&& e.switchPort.equals(swp.getPortId())) {
 				if (e.getVlan() == null)
 					vals.add(VlanVid.ZERO);
 				else
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java
index 199271d14506f25b173648e2741a97f20a1f491b..73249879cf730771fc23cb4538d4107e66602bbe 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java
@@ -123,11 +123,11 @@ public class DeviceIterator extends FilterIterator<Device> {
             match = false;
             for (SwitchPort sp : sps) {
                 if (!switchDPID.equals(DatapathId.NONE)) {
-                    if (!switchDPID.equals(sp.getSwitchDPID()))
+                    if (!switchDPID.equals(sp.getNodeId()))
                         return false;
                 }
                 if (!switchPort.equals(OFPort.ZERO)) {
-                    if (!switchPort.equals(sp.getPort()))
+                    if (!switchPort.equals(sp.getPortId()))
                         return false;
                 }
                 match = true;
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
index aa7de58f6a43fa3a9cc58ad43f5f1814b61c7c50..7fffeeb3edaa109d93ba7d561e38fd893454135d 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
@@ -18,7 +18,6 @@
 package net.floodlightcontroller.devicemanager.internal;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
@@ -57,12 +56,6 @@ import net.floodlightcontroller.core.util.ListenerDispatcher;
 import net.floodlightcontroller.core.util.SingletonTask;
 import net.floodlightcontroller.debugcounter.IDebugCounter;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
-import net.floodlightcontroller.debugevent.DebugEventService.EventCategoryBuilder;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventType;
-import net.floodlightcontroller.debugevent.IEventCategory;
 import net.floodlightcontroller.devicemanager.IDevice;
 import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.devicemanager.IEntityClass;
@@ -163,12 +156,6 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 	public IDebugCounter cntConsolidateStoreDevicesRemoved;
 	public IDebugCounter cntTransitionToMaster;
 
-	/**
-	 * Debug Events
-	 */
-	private IDebugEventService debugEventService;
-	private IEventCategory<DeviceEvent> debugEventCategory;
-
 	private boolean isMaster = false;
 
 	static final String DEVICE_SYNC_STORE_NAME =
@@ -749,76 +736,6 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		}
 	}
 
-	// ***************
-	// IDeviceListener
-	// ***************
-	private class DeviceDebugEventLogger implements IDeviceListener {
-		@Override
-		public String getName() {
-			return "deviceDebugEventLogger";
-		}
-
-		@Override
-		public boolean isCallbackOrderingPrereq(String type, String name) {
-			return false;
-		}
-
-		@Override
-		public boolean isCallbackOrderingPostreq(String type, String name) {
-			return false;
-		}
-
-		@Override
-		public void deviceAdded(IDevice device) {
-			generateDeviceEvent(device, "host-added");
-		}
-
-		@Override
-		public void deviceRemoved(IDevice device) {
-			generateDeviceEvent(device, "host-removed");
-		}
-
-		@Override
-		public void deviceMoved(IDevice device) {
-			generateDeviceEvent(device, "host-moved");
-		}
-
-		@Override
-		public void deviceIPV4AddrChanged(IDevice device) {
-			generateDeviceEvent(device, "host-ipv4-addr-changed");
-		}
-		
-		@Override
-		public void deviceIPV6AddrChanged(IDevice device) {
-			generateDeviceEvent(device, "host-ipv6-addr-changed");
-		}
-
-		@Override
-		public void deviceVlanChanged(IDevice device) {
-			generateDeviceEvent(device, "host-vlan-changed");
-		}
-
-		private void generateDeviceEvent(IDevice device, String reason) {
-			List<IPv4Address> ipv4Addresses =
-					new ArrayList<IPv4Address>(Arrays.asList(device.getIPv4Addresses()));
-			List<IPv6Address> ipv6Addresses =
-					new ArrayList<IPv6Address>(Arrays.asList(device.getIPv6Addresses()));
-			List<SwitchPort> oldAps =
-					new ArrayList<SwitchPort>(Arrays.asList(device.getOldAP()));
-			List<SwitchPort> currentAps =
-					new ArrayList<SwitchPort>(Arrays.asList(device.getAttachmentPoints()));
-			List<VlanVid> vlanIds =
-					new ArrayList<VlanVid>(Arrays.asList(device.getVlanId()));
-
-			debugEventCategory.newEventNoFlush(new DeviceEvent(device.getMACAddress(),
-					ipv4Addresses,
-					ipv6Addresses,
-					oldAps,
-					currentAps,
-					vlanIds, reason));
-		}
-	}
-
 	// *************
 	// IInfoProvider
 	// *************
@@ -925,27 +842,10 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		this.threadPool = fmc.getServiceImpl(IThreadPoolService.class);
 		this.entityClassifier = fmc.getServiceImpl(IEntityClassifierService.class);
 		this.debugCounters = fmc.getServiceImpl(IDebugCounterService.class);
-		this.debugEventService = fmc.getServiceImpl(IDebugEventService.class);
 		this.syncService = fmc.getServiceImpl(ISyncService.class);
 		this.deviceSyncManager = new DeviceSyncManager();
 		this.haListenerDelegate = new HAListenerDelegate();
 		registerDeviceManagerDebugCounters();
-		registerDeviceManagerDebugEvents();
-		this.addListener(new DeviceDebugEventLogger());
-	}
-
-	private void registerDeviceManagerDebugEvents() throws FloodlightModuleException {
-		if (debugEventService == null) {
-			logger.error("debugEventService should not be null");
-		}
-		EventCategoryBuilder<DeviceEvent> ecb = debugEventService.buildEvent(DeviceEvent.class);
-		debugEventCategory = ecb.setModuleName(PACKAGE)
-				.setEventName("hostevent")
-				.setEventDescription("Host added, removed, updated, or moved")
-				.setEventType(EventType.ALWAYS_LOG)
-				.setBufferCapacity(500)
-				.setAckable(false)
-				.register();
 	}
 
 	@Override
@@ -2026,9 +1926,6 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 				 break;
 			 }
 		 }
-		 // Since cleanupEntities() is not called in the packet-in pipeline,
-		 // debugEvents need to be flushed explicitly
-		 debugEventService.flushEvents();
 	 }
 
 	 protected void removeEntity(Entity removed,
@@ -2181,9 +2078,6 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 				 sendDeviceMovedNotification(d);
 			 }
 		 }
-		 // Since topologyChanged() does not occur in the packet-in pipeline,
-		 // debugEvents need to be flushed explicitly
-		 debugEventService.flushEvents();
 	 }
 
 	 /**
@@ -2262,9 +2156,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 		 for (Entity entity: device.entities ) {
 			 this.learnDeviceByEntity(entity);
 		 }
-		 // Since reclassifyDevices() is not called in the packet-in pipeline,
-		 // debugEvents need to be flushed explicitly
-		 debugEventService.flushEvents();
+
 		 return true;
 	 }
 
@@ -2540,41 +2432,4 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT
 	 IHAListener getHAListener() {
 		 return this.haListenerDelegate;
 	 }
-
-	 /**
-	  * Device Event Class used to log Device related events
-	  */
-	 private class DeviceEvent {
-		 @EventColumn(name = "MAC", description = EventFieldType.MAC)
-		 private final MacAddress macAddress;
-		 @EventColumn(name = "IPv4s", description = EventFieldType.IPv4)
-		 private final List<IPv4Address> ipv4Addresses;
-		 @EventColumn(name = "IPv6s", description = EventFieldType.IPv6)
-		 private final List<IPv6Address> ipv6Addresses;
-		 @EventColumn(name = "Old Attachment Points",
-				 description = EventFieldType.COLLECTION_ATTACHMENT_POINT)
-		 private final List<SwitchPort> oldAttachmentPoints;
-		 @EventColumn(name = "Current Attachment Points",
-				 description = EventFieldType.COLLECTION_ATTACHMENT_POINT)
-		 private final List<SwitchPort> currentAttachmentPoints;
-		 @EventColumn(name = "VLAN IDs", description = EventFieldType.COLLECTION_OBJECT)
-		 private final List<VlanVid> vlanIds;
-		 @EventColumn(name = "Reason", description = EventFieldType.STRING)
-		 private final String reason;
-
-		 public DeviceEvent(MacAddress macAddress, List<IPv4Address> ipv4Addresses,
-				 List<IPv6Address> ipv6Addresses,
-				 List<SwitchPort> oldAttachmentPoints,
-				 List<SwitchPort> currentAttachmentPoints,
-				 List<VlanVid> vlanIds, String reason) {
-			 super();
-			 this.macAddress = macAddress;
-			 this.ipv4Addresses = ipv4Addresses;
-			 this.ipv6Addresses = ipv6Addresses;
-			 this.oldAttachmentPoints = oldAttachmentPoints;
-			 this.currentAttachmentPoints = currentAttachmentPoints;
-			 this.vlanIds = vlanIds;
-			 this.reason = reason;
-		 }
-	 }
 }
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java
index c600ab2cfabe298cc1ecfab981edab44ec4ee2be..01aa65eadbd3307a29a62c592a68ec1851163ac9 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java
@@ -111,8 +111,8 @@ public class DeviceSyncRepresentation {
 		if (!e.hasSwitchPort())
 			return false;
 		for (SwitchPort p : aps) {
-			if (e.getSwitchDPID().equals(p.getSwitchDPID()) &&
-					e.getSwitchPort().equals(p.getPort())
+			if (e.getSwitchDPID().equals(p.getNodeId()) &&
+					e.getSwitchPort().equals(p.getPortId())
 					&& (!e.getSwitchDPID().equals(DatapathId.NONE) ||
 					!e.getSwitchPort().equals(OFPort.ZERO))) {
 				return true;
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java b/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java
index 595650f45ad385c76c4f5cf49148102a877c9d25..fc9da07e1c3d7dbb3531eae2e24949b60747398b 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java
@@ -203,7 +203,7 @@ public abstract class AbstractDeviceResource extends ServerResource {
                     for (SwitchPort v : value.getAttachmentPoints(true)) {
                         String str;
                         if (v != null && 
-                            (str = v.getSwitchDPID().toString()) != null &&
+                            (str = v.getNodeId().toString()) != null &&
                             str.startsWith(dpidStartsWith)) {
                             match = true;
                             break;
@@ -216,7 +216,7 @@ public abstract class AbstractDeviceResource extends ServerResource {
                     for (SwitchPort v : value.getAttachmentPoints(true)) {
                         String str;
                         if (v != null && 
-                            (str = v.getPort().toString()) != null &&
+                            (str = v.getPortId().toString()) != null &&
                             str.startsWith(portStartsWith)) {
                             match = true;
                             break;
diff --git a/src/main/java/net/floodlightcontroller/dhcpserver/DHCPInstance.java b/src/main/java/net/floodlightcontroller/dhcpserver/DHCPInstance.java
new file mode 100644
index 0000000000000000000000000000000000000000..65f6e0fdb460189ade44e47fe56b01aadfd4eebd
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/dhcpserver/DHCPInstance.java
@@ -0,0 +1,398 @@
+package net.floodlightcontroller.dhcpserver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import net.floodlightcontroller.core.types.NodePortTuple;
+
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.VlanVid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DHCPInstance {
+	private static final Logger log = LoggerFactory.getLogger(DHCPInstance.class);
+	
+	private String name = null;
+	private volatile DHCPPool pool = null;
+	private Set<NodePortTuple> memberPorts = null;
+	private Set<VlanVid> memberVlans = null;
+	
+	private IPv4Address serverIp = IPv4Address.NONE;
+	private MacAddress serverMac = MacAddress.NONE;
+	private IPv4Address subnetMask = IPv4Address.NONE;
+	private IPv4Address broadcastIp = IPv4Address.NONE;
+	private IPv4Address routerIp = IPv4Address.NONE;
+	private List<IPv4Address> ntpIps = null;
+	private List<IPv4Address> dnsIps = null;
+	
+	private String domainName = null;
+	private boolean ipForwarding = false;
+	
+	private int leaseTimeSec = 0;
+	public static final int HOLD_LEASE_TIME_SEC = 10;
+	
+	/* Computed */
+	private int rebindTimeSec = 0;
+	private int renewalTimeSec = 0;
+	
+	private DHCPInstance() {			
+		ipForwarding = false;
+	}
+	
+	public static DHCPInstanceBuilder createBuilder() {
+		return new DHCPInstanceBuilder();
+	}
+	
+	public String getName() {
+		return name;
+	}
+	public IPv4Address getServerIp() {
+		return serverIp;
+	}
+	public MacAddress getServerMac() {
+		return serverMac;
+	}
+	public IPv4Address getSubnetMask() {
+		return subnetMask;
+	}
+	public IPv4Address getBroadcastIp() {
+		return broadcastIp;
+	}
+	public IPv4Address getRouterIp() {
+		return routerIp;
+	}
+	public List<IPv4Address> getNtpIps() {
+		return Collections.unmodifiableList(ntpIps);
+	}
+	public List<IPv4Address> getDnsIps() {
+		return Collections.unmodifiableList(dnsIps);
+	}
+	public String getDomainName() {
+		return domainName;
+	}
+	public boolean getIpForwarding() {
+		return ipForwarding;
+	}
+	public int getLeaseTimeSec() {
+		return leaseTimeSec;
+	}
+	public int getRenewalTimeSec() {
+		return renewalTimeSec;
+	}
+	public int getRebindTimeSec() {
+		return rebindTimeSec;
+	}
+	public int getHoldTimeSec() {
+		return HOLD_LEASE_TIME_SEC;
+	}
+	public DHCPPool getPool() {
+		return pool;
+	}
+	public Set<NodePortTuple> getMemberPorts() {
+		return Collections.unmodifiableSet(memberPorts);
+	}
+	public Set<VlanVid> getMemberVlans() {
+		return Collections.unmodifiableSet(memberVlans);
+	}
+	
+	public static class DHCPInstanceBuilder {
+		private DHCPInstance tempInstance = null;
+		private Map<MacAddress, IPv4Address> tempStaticAddresses = null;
+		private IPv4Address tempStart = IPv4Address.NONE;
+		private IPv4Address tempStop = IPv4Address.NONE;
+		
+		private DHCPInstanceBuilder() {
+			tempInstance = new DHCPInstance();
+			tempStaticAddresses = new HashMap<MacAddress, IPv4Address>();
+		}
+		
+		public DHCPInstanceBuilder setName(String name) {
+			if (name == null || name.isEmpty()) {
+				throw new IllegalArgumentException("DHCP server name cannot be null or empty");
+			}
+			tempInstance.name = name;
+			return this;
+		}
+		public DHCPInstanceBuilder setServer(MacAddress mac, IPv4Address ip) {
+			if (ip == null || mac == null) {
+				throw new IllegalArgumentException("DHCP server IP/Mac cannot be null");
+			}
+			tempInstance.serverIp = ip;
+			tempInstance.serverMac = mac;
+			return this;
+		}
+		public DHCPInstanceBuilder setSubnetMask(IPv4Address mask) {
+			if (mask == null) {
+				throw new IllegalArgumentException("DHCP server subnet mask cannot be null");
+			}
+			tempInstance.subnetMask = mask;
+			return this;
+		}
+		public DHCPInstanceBuilder setBroadcastIp(IPv4Address bcast) {
+			if (bcast == null) {
+				throw new IllegalArgumentException("DHCP server broadcast IP cannot be null");
+			}
+			tempInstance.broadcastIp = bcast;
+			return this;
+		}
+		public DHCPInstanceBuilder setStartIp(IPv4Address start) {
+			if (start == null) {
+				throw new IllegalArgumentException("DHCP pool start IP cannot be null");
+			}
+			tempStart = start;
+			return this;
+		}
+		public DHCPInstanceBuilder setStopIp(IPv4Address stop) {
+			if (stop == null) {
+				throw new IllegalArgumentException("DHCP pool stop IP cannot be null");
+			}
+			tempStop = stop;
+			return this;
+		}
+		public DHCPInstanceBuilder setRouterIp(IPv4Address router) {
+			if (router == null) {
+				throw new IllegalArgumentException("DHCP server router/gateway IP cannot be null");
+			}
+			tempInstance.routerIp = router;
+			return this;
+		}
+		public DHCPInstanceBuilder setNtpIps(List<IPv4Address> ntps) {
+			if (ntps == null) {
+				throw new IllegalArgumentException("DHCP server NTP IP list cannot be null");
+			}
+			tempInstance.ntpIps = ntps;
+			return this;
+		}
+		public DHCPInstanceBuilder setDnsIps(List<IPv4Address> dnss) {
+			if (dnss == null) {
+				throw new IllegalArgumentException("DHCP server DNS IP list cannot be null");
+			}
+			tempInstance.dnsIps = dnss;
+			return this;
+		}
+		public DHCPInstanceBuilder setDomainName(String dn) {
+			if (dn == null) {
+				throw new IllegalArgumentException("DHCP server domain name cannot be null");
+			}
+			tempInstance.domainName = dn;
+			return this;
+		}
+		public DHCPInstanceBuilder setIpForwarding(boolean on) {
+			tempInstance.ipForwarding = on;
+			return this;
+		}
+		public DHCPInstanceBuilder setLeaseTimeSec(int s) {
+			if (s <= 0) {
+				throw new IllegalArgumentException("DHCP server lease time must be > 0");
+			}
+			if (s < 3600) {
+				log.warn("DHCP server lease time set less than one hour at {} seconds", s);
+			}
+			tempInstance.leaseTimeSec = s;
+			return this;
+		}
+		public DHCPInstanceBuilder addReservedStaticAddress(MacAddress rsvMac, IPv4Address rsvIp) {
+			tempStaticAddresses.put(rsvMac, rsvIp);
+			return this;
+		}
+		public DHCPInstanceBuilder setMemberPorts(Set<NodePortTuple> ports) {
+			if (ports == null) {
+				throw new IllegalArgumentException("DHCP server member ports cannot be null");
+			}
+			tempInstance.memberPorts = ports;
+			return this;
+		}
+		public DHCPInstanceBuilder setMemberVlans(Set<VlanVid> vlans) {
+			if (vlans == null) {
+				throw new IllegalArgumentException("DHCP server member VLANs cannot be null");
+			}
+			tempInstance.memberVlans = vlans;
+			return this;
+		}
+		
+		public DHCPInstance build() {
+			DHCPInstance instanceToReturn = new DHCPInstance();
+
+			if (tempStart.compareTo(tempStop) > 0) {
+				throw new IllegalArgumentException("Start IP must be less than stop IP");
+			}
+			instanceToReturn.pool = new DHCPPool(tempStart, tempStop.getInt() - tempStart.getInt() + 1);
+			for (Entry<MacAddress, IPv4Address> entry : tempStaticAddresses.entrySet()) {
+				instanceToReturn.pool.configureFixedIPLease(entry.getValue(), entry.getKey());
+			}
+			
+			if (tempInstance.leaseTimeSec <= 0) {
+				throw new IllegalArgumentException("DHCP server lease time must be > 0");
+			}
+			instanceToReturn.leaseTimeSec = tempInstance.leaseTimeSec;
+			instanceToReturn.rebindTimeSec = (int) (instanceToReturn.leaseTimeSec / 0.875);
+			instanceToReturn.renewalTimeSec = instanceToReturn.leaseTimeSec / 2;
+			
+			// TODO check to make sure these are in the same subnet
+			instanceToReturn.broadcastIp = tempInstance.broadcastIp; /* IPv4/MacAddress are immutable */
+			instanceToReturn.serverMac = tempInstance.serverMac;
+			instanceToReturn.serverIp = tempInstance.serverIp;
+			instanceToReturn.routerIp = tempInstance.routerIp;
+			instanceToReturn.subnetMask = tempInstance.subnetMask;
+			
+			instanceToReturn.domainName = tempInstance.domainName;
+			
+			if (tempInstance.dnsIps == null) {
+				instanceToReturn.dnsIps = new ArrayList<IPv4Address>();
+			} else {
+				instanceToReturn.dnsIps = new ArrayList<IPv4Address>(tempInstance.dnsIps);
+			}
+			if (tempInstance.ntpIps == null) {
+				instanceToReturn.ntpIps = new ArrayList<IPv4Address>();
+			} else {
+				instanceToReturn.ntpIps = new ArrayList<IPv4Address>(tempInstance.ntpIps);
+			}
+			if (tempInstance.memberPorts == null) {
+				instanceToReturn.memberPorts = new HashSet<NodePortTuple>();
+			} else {
+				instanceToReturn.memberPorts = new HashSet<NodePortTuple>(tempInstance.memberPorts);
+			}
+			if (tempInstance.memberVlans == null) {
+				instanceToReturn.memberVlans = new HashSet<VlanVid>();
+			} else {
+				instanceToReturn.memberVlans = new HashSet<VlanVid>(tempInstance.memberVlans);
+			}
+			
+			instanceToReturn.ipForwarding = tempInstance.ipForwarding;
+			
+			return instanceToReturn;
+		}	
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((broadcastIp == null) ? 0 : broadcastIp.hashCode());
+		result = prime * result + ((dnsIps == null) ? 0 : dnsIps.hashCode());
+		result = prime * result
+				+ ((domainName == null) ? 0 : domainName.hashCode());
+		result = prime * result + (ipForwarding ? 1231 : 1237);
+		result = prime * result + leaseTimeSec;
+		result = prime * result
+				+ ((memberPorts == null) ? 0 : memberPorts.hashCode());
+		result = prime * result
+				+ ((memberVlans == null) ? 0 : memberVlans.hashCode());
+		result = prime * result + ((name == null) ? 0 : name.hashCode());
+		result = prime * result + ((ntpIps == null) ? 0 : ntpIps.hashCode());
+		result = prime * result + ((pool == null) ? 0 : pool.hashCode());
+		result = prime * result + rebindTimeSec;
+		result = prime * result + renewalTimeSec;
+		result = prime * result
+				+ ((routerIp == null) ? 0 : routerIp.hashCode());
+		result = prime * result
+				+ ((serverIp == null) ? 0 : serverIp.hashCode());
+		result = prime * result
+				+ ((serverMac == null) ? 0 : serverMac.hashCode());
+		result = prime * result
+				+ ((subnetMask == null) ? 0 : subnetMask.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		DHCPInstance other = (DHCPInstance) obj;
+		if (broadcastIp == null) {
+			if (other.broadcastIp != null)
+				return false;
+		} else if (!broadcastIp.equals(other.broadcastIp))
+			return false;
+		if (dnsIps == null) {
+			if (other.dnsIps != null)
+				return false;
+		} else if (!dnsIps.equals(other.dnsIps))
+			return false;
+		if (domainName == null) {
+			if (other.domainName != null)
+				return false;
+		} else if (!domainName.equals(other.domainName))
+			return false;
+		if (ipForwarding != other.ipForwarding)
+			return false;
+		if (leaseTimeSec != other.leaseTimeSec)
+			return false;
+		if (memberPorts == null) {
+			if (other.memberPorts != null)
+				return false;
+		} else if (!memberPorts.equals(other.memberPorts))
+			return false;
+		if (memberVlans == null) {
+			if (other.memberVlans != null)
+				return false;
+		} else if (!memberVlans.equals(other.memberVlans))
+			return false;
+		if (name == null) {
+			if (other.name != null)
+				return false;
+		} else if (!name.equals(other.name))
+			return false;
+		if (ntpIps == null) {
+			if (other.ntpIps != null)
+				return false;
+		} else if (!ntpIps.equals(other.ntpIps))
+			return false;
+		if (pool == null) {
+			if (other.pool != null)
+				return false;
+		} else if (!pool.equals(other.pool))
+			return false;
+		if (rebindTimeSec != other.rebindTimeSec)
+			return false;
+		if (renewalTimeSec != other.renewalTimeSec)
+			return false;
+		if (routerIp == null) {
+			if (other.routerIp != null)
+				return false;
+		} else if (!routerIp.equals(other.routerIp))
+			return false;
+		if (serverIp == null) {
+			if (other.serverIp != null)
+				return false;
+		} else if (!serverIp.equals(other.serverIp))
+			return false;
+		if (serverMac == null) {
+			if (other.serverMac != null)
+				return false;
+		} else if (!serverMac.equals(other.serverMac))
+			return false;
+		if (subnetMask == null) {
+			if (other.subnetMask != null)
+				return false;
+		} else if (!subnetMask.equals(other.subnetMask))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		return "DHCPInstance [name=" + name + ", pool=" + pool
+				+ ", memberPorts=" + memberPorts + ", memberVlans="
+				+ memberVlans + ", serverIp=" + serverIp + ", serverMac="
+				+ serverMac + ", subnetMask=" + subnetMask + ", broadcastIp="
+				+ broadcastIp + ", routerIp=" + routerIp + ", ntpIps=" + ntpIps
+				+ ", dnsIps=" + dnsIps + ", domainName=" + domainName
+				+ ", ipForwarding=" + ipForwarding + ", leaseTimeSec="
+				+ leaseTimeSec + ", rebindTimeSec=" + rebindTimeSec
+				+ ", renewalTimeSec=" + renewalTimeSec + "]";
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/dhcpserver/DHCPPool.java b/src/main/java/net/floodlightcontroller/dhcpserver/DHCPPool.java
index 2c04c49fc1367fc7ce924db533679176286392ed..4c7b4ac0bcde1ef4b7d8973e2e797cd80c2e4b1d 100644
--- a/src/main/java/net/floodlightcontroller/dhcpserver/DHCPPool.java
+++ b/src/main/java/net/floodlightcontroller/dhcpserver/DHCPPool.java
@@ -5,6 +5,7 @@ import java.util.ArrayList;
 import org.projectfloodlight.openflow.types.IPv4Address;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import net.floodlightcontroller.dhcpserver.DHCPBinding;
 
@@ -15,7 +16,7 @@ import net.floodlightcontroller.dhcpserver.DHCPBinding;
  * @author Ryan Izard (rizard@g.clemson.edu)
  */
 public class DHCPPool {
-	protected Logger log;
+	protected static final Logger log = LoggerFactory.getLogger(DHCPPool.class);;
 	private volatile static ArrayList<DHCPBinding> DHCP_POOL = new ArrayList<DHCPBinding>();
 	private volatile int POOL_SIZE;
 	private volatile int POOL_AVAILABILITY;
@@ -32,8 +33,7 @@ public class DHCPPool {
 	 * @param {@code integer} size: (startingIPv4Address + size) is the highest IP address to lease.
 	 * @return none
 	 */
-	public DHCPPool(IPv4Address startingIPv4Address, int size, Logger log) {
-		this.log = log;
+	public DHCPPool(IPv4Address startingIPv4Address, int size) {
 		int IPv4AsInt = startingIPv4Address.getInt();
 		this.setPoolSize(size);
 		this.setPoolAvailability(size);
@@ -43,6 +43,10 @@ public class DHCPPool {
 		}
 
 	}
+	
+	public IPv4Address getStartIp() {
+		return STARTING_ADDRESS;
+	}
 
 	private void setPoolFull(boolean full) {
 		POOL_FULL = full;
@@ -332,4 +336,46 @@ public class DHCPPool {
 		}
 	}
 
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + POOL_AVAILABILITY;
+		result = prime * result + (POOL_FULL ? 1231 : 1237);
+		result = prime * result + POOL_SIZE;
+		result = prime
+				* result
+				+ ((STARTING_ADDRESS == null) ? 0 : STARTING_ADDRESS.hashCode());
+		result = prime * result
+				+ ((UNASSIGNED_MAC == null) ? 0 : UNASSIGNED_MAC.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		DHCPPool other = (DHCPPool) obj;
+		if (POOL_AVAILABILITY != other.POOL_AVAILABILITY)
+			return false;
+		if (POOL_FULL != other.POOL_FULL)
+			return false;
+		if (POOL_SIZE != other.POOL_SIZE)
+			return false;
+		if (STARTING_ADDRESS == null) {
+			if (other.STARTING_ADDRESS != null)
+				return false;
+		} else if (!STARTING_ADDRESS.equals(other.STARTING_ADDRESS))
+			return false;
+		if (UNASSIGNED_MAC == null) {
+			if (other.UNASSIGNED_MAC != null)
+				return false;
+		} else if (!UNASSIGNED_MAC.equals(other.UNASSIGNED_MAC))
+			return false;
+		return true;
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/dhcpserver/DHCPServer.java b/src/main/java/net/floodlightcontroller/dhcpserver/DHCPServer.java
index fce5f4a1a6d7625ba3b0900a8b1564072ae3010e..3f476124a2a20b5af935410f9af048bd9e8043eb 100644
--- a/src/main/java/net/floodlightcontroller/dhcpserver/DHCPServer.java
+++ b/src/main/java/net/floodlightcontroller/dhcpserver/DHCPServer.java
@@ -1,8 +1,12 @@
 package net.floodlightcontroller.dhcpserver;
 
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -12,15 +16,15 @@ import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
 import org.projectfloodlight.openflow.protocol.OFPacketOut;
 import org.projectfloodlight.openflow.protocol.OFType;
-import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
-import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
 import org.projectfloodlight.openflow.types.IpProtocol;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.VlanVid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,18 +37,19 @@ import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.forwarding.Forwarding;
+import net.floodlightcontroller.core.types.NodePortTuple;
+import net.floodlightcontroller.dhcpserver.DHCPInstance.DHCPInstanceBuilder;
 import net.floodlightcontroller.packet.DHCP.DHCPOptionCode;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.packet.UDP;
 import net.floodlightcontroller.packet.DHCP;
 import net.floodlightcontroller.packet.DHCPOption;
+import net.floodlightcontroller.util.OFMessageUtils;
 
 /**
  * SDN DHCP Server
- * @author Ryan Izard, rizard@g.clemson.edu
- * 
+ * @author Ryan Izard, rizard@g.clemson.edu, ryan.izard@bigswitch.com
  * 
  * The Floodlight Module implementing a DHCP DHCPServer.
  * This module uses {@code DHCPPool} to manage DHCP leases.
@@ -53,8 +58,10 @@ import net.floodlightcontroller.packet.DHCPOption;
  * 
  * 		floodlight/src/main/resources/floodlightdefault.properties
  * 
- * contains the DHCP options and parameters that can be set. To allow
- * all DHCP request messages to be sent to the controller (Floodlight),
+ * contains the DHCP options and parameters that can be set for a single
+ * subnet. Multiple subnets can be configured with the REST API.
+ * 
+ * To allow all DHCP request messages to be sent to the controller,
  * the DHCPSwitchFlowSetter module (in this same package) and the
  * Forwarding module (loaded by default) should also be loaded in
  * Floodlight. When the first DHCP request is received on a particular
@@ -73,64 +80,39 @@ import net.floodlightcontroller.packet.DHCPOption;
  * to be intercepted on that same port and sent to the DHCP server running
  * on the Floodlight controller.
  * 
- * Currently, this DHCP server only supports a single subnet; however,
- * work is ongoing to use connected OF switches and ports to allow
- * the user to configure multiple subnets. On a traditional DHCP server,
- * the machine is configured with different NICs, each with their own
- * statically-assigned IP address/subnet/mask. The DHCP server matches
- * the network information of each NIC with the DHCP server's configured
- * subnets and answers the requests accordingly. To mirror this behavior
- * on a OpenFlow network, we can differentiate between subnets based on a
- * device's attachment point. We can assign subnets for a device per
- * OpenFlow switch or per port per switch. This is the next step for
- * this implementations of a SDN DHCP server.
+ * On a traditional DHCP server, the machine is configured with different 
+ * NICs, each with their own statically-assigned IP address/subnet/mask. 
+ * The DHCP server matches the network information of each NIC with the DHCP 
+ * server's configured subnets and answers the requests accordingly. To 
+ * mirror this behavior on a OF network, we can differentiate between subnets 
+ * based on a device's attachment point. We can assign subnets for a device
+ * per OpenFlow switch or per port per switch.
  *
  * I welcome any feedback or suggestions for improvement!
  * 
  * 
  */
-public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
-	protected static Logger log;
+public class DHCPServer implements IOFMessageListener, IFloodlightModule, IDHCPService {
+	protected static final Logger log = LoggerFactory.getLogger(DHCPServer.class);
+
 	protected static IFloodlightProviderService floodlightProvider;
 	protected static IOFSwitchService switchService;
 
-	// The garbage collector service for the DHCP server
-	// Handle expired leases by adding the IP back to the address pool
+	private static Map<String, DHCPInstance> instances;
+	private static volatile boolean enabled = false;
+
+	/*
+	 * The garbage collector service for the DHCP server. It handles
+	 * expired leases by adding the IPs back to the address pool.
+	 */
 	private static ScheduledThreadPoolExecutor leasePoliceDispatcher;
-	//private static ScheduledFuture<?> leasePoliceOfficer;
 	private static Runnable leasePolicePatrol;
 
-	// Contains the pool of IP addresses their bindings to MAC addresses
-	// Tracks the lease status and duration of DHCP bindings
-	private static volatile DHCPPool theDHCPPool;
-
-	/** START CONFIG FILE VARIABLES **/
-
-	// These variables are set using the floodlightdefault.properties file
-	// Refer to startup() for a list of the expected names in the config file
-
-	// The IP and MAC addresses of the controller/DHCP server
-	private static MacAddress CONTROLLER_MAC;
-	private static IPv4Address CONTROLLER_IP;
-
-	private static IPv4Address DHCP_SERVER_DHCP_SERVER_IP; // Same as CONTROLLER_IP but in byte[] form
-	private static IPv4Address DHCP_SERVER_SUBNET_MASK;
-	private static IPv4Address DHCP_SERVER_BROADCAST_IP;
-	private static IPv4Address DHCP_SERVER_IP_START;
-	private static IPv4Address DHCP_SERVER_IP_STOP;
-	private static int DHCP_SERVER_ADDRESS_SPACE_SIZE; // Computed in startUp()
-	private static IPv4Address DHCP_SERVER_ROUTER_IP = null;
-	private static byte[] DHCP_SERVER_NTP_IP_LIST = null;
-	private static byte[] DHCP_SERVER_DNS_IP_LIST = null;
-	private static byte[] DHCP_SERVER_DN = null;
-	private static byte[] DHCP_SERVER_IP_FORWARDING = null;
-	private static int DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS;
-	private static int DHCP_SERVER_HOLD_LEASE_TIME_SECONDS;
-	private static int DHCP_SERVER_REBIND_TIME_SECONDS; // Computed in startUp()
-	private static int DHCP_SERVER_RENEWAL_TIME_SECONDS; // Computed in startUp()
+	/* Variables set from floodlightdefault.properties */
+
 	private static long DHCP_SERVER_LEASE_POLICE_PATROL_PERIOD_SECONDS;
 
-	/** END CONFIG FILE VARIABLES **/
+	/* end floodlightdefault.properties variables */
 
 	/**
 	 * DHCP messages are either:
@@ -215,7 +197,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 	public static final MacAddress BROADCAST_MAC = MacAddress.BROADCAST;
 	public static final IPv4Address BROADCAST_IP = IPv4Address.NO_MASK; /* no_mask is all 1's */
 	public static final IPv4Address UNASSIGNED_IP = IPv4Address.FULL_MASK; /* full_mask is all 0's */
-	
+
 	@Override
 	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
 		Collection<Class<? extends IFloodlightService>> l = 
@@ -228,7 +210,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 	public void init(FloodlightModuleContext context) throws FloodlightModuleException {
 		floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
 		switchService = context.getServiceImpl(IOFSwitchService.class);
-		log = LoggerFactory.getLogger(DHCPServer.class);
+		instances = new HashMap<String, DHCPInstance>();
 	}
 
 	@Override
@@ -237,24 +219,19 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 
 		// Read our config options for the DHCP DHCPServer
 		Map<String, String> configOptions = context.getConfigParams(this);
+		DHCPInstanceBuilder instanceBuilder = DHCPInstance.createBuilder();
 		try {
-			DHCP_SERVER_SUBNET_MASK = IPv4Address.of(configOptions.get("subnet-mask"));
-			DHCP_SERVER_IP_START = IPv4Address.of(configOptions.get("lower-ip-range"));
-			DHCP_SERVER_IP_STOP = IPv4Address.of(configOptions.get("upper-ip-range"));
-			DHCP_SERVER_ADDRESS_SPACE_SIZE = DHCP_SERVER_IP_STOP.getInt() - DHCP_SERVER_IP_START.getInt() + 1;
-			DHCP_SERVER_BROADCAST_IP = IPv4Address.of(configOptions.get("broadcast-address"));
-			DHCP_SERVER_ROUTER_IP = IPv4Address.of(configOptions.get("router"));
-			DHCP_SERVER_DN = configOptions.get("domain-name").getBytes();
-			DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS = Integer.parseInt(configOptions.get("default-lease-time"));
-			DHCP_SERVER_HOLD_LEASE_TIME_SECONDS = Integer.parseInt(configOptions.get("hold-lease-time"));
-			DHCP_SERVER_RENEWAL_TIME_SECONDS = (int) (DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS / 2.0);
-			DHCP_SERVER_REBIND_TIME_SECONDS = (int) (DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS * 0.875);
-			DHCP_SERVER_LEASE_POLICE_PATROL_PERIOD_SECONDS = Long.parseLong(configOptions.get("lease-gc-period"));
-			DHCP_SERVER_IP_FORWARDING = intToBytesSizeOne(Integer.parseInt(configOptions.get("ip-forwarding")));
+			instanceBuilder.setSubnetMask(IPv4Address.of(configOptions.get("subnet-mask")))
+			.setStartIp(IPv4Address.of(configOptions.get("lower-ip-range")))
+			.setStopIp(IPv4Address.of(configOptions.get("upper-ip-range")))
+			.setBroadcastIp(IPv4Address.of(configOptions.get("broadcast-address")))
+			.setRouterIp(IPv4Address.of(configOptions.get("router")))
+			.setDomainName(configOptions.get("domain-name"))
+			.setLeaseTimeSec(Integer.parseInt(configOptions.get("default-lease-time")))
+			.setIpForwarding(Boolean.parseBoolean(configOptions.get("ip-forwarding")))
+			.setServer(MacAddress.of(configOptions.get("controller-mac")), IPv4Address.of(configOptions.get("controller-ip")));
 
-			CONTROLLER_MAC = MacAddress.of(configOptions.get("controller-mac"));
-			CONTROLLER_IP = IPv4Address.of(configOptions.get("controller-ip"));
-			DHCP_SERVER_DHCP_SERVER_IP = CONTROLLER_IP;
+			DHCP_SERVER_LEASE_POLICE_PATROL_PERIOD_SECONDS = Long.parseLong(configOptions.get("lease-gc-period"));
 
 			// NetBios and other options can be added to this function here as needed in the future
 		} catch(IllegalArgumentException ex) {
@@ -263,9 +240,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		} catch(NullPointerException ex) {
 			log.error("Incorrect DHCP Server configuration options", ex);
 			throw ex;
-		}
-		// Create our new DHCPPool object with the specific address size
-		theDHCPPool = new DHCPPool(DHCP_SERVER_IP_START, DHCP_SERVER_ADDRESS_SPACE_SIZE, log);
+		}		
 
 		// Any addresses that need to be set as static/fixed can be permanently added to the pool with a set MAC
 		String staticAddresses = configOptions.get("reserved-static-addresses");
@@ -285,51 +260,76 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 					macPos = 1;
 					ipPos = 0;
 				}
-				if (theDHCPPool.configureFixedIPLease(IPv4Address.of(macIpSplit[ipPos]), MacAddress.of(macIpSplit[macPos]))) {
-					String ip = theDHCPPool.getDHCPbindingFromIPv4(IPv4Address.of(macIpSplit[ipPos])).getIPv4Address().toString();
-					String mac = theDHCPPool.getDHCPbindingFromIPv4(IPv4Address.of(macIpSplit[ipPos])).getMACAddress().toString();
-					log.info("Configured fixed address of " + ip + " for device " + mac);
-				} else {
-					log.error("Could not configure fixed address " + macIpSplit[ipPos] + " for device " + macIpSplit[macPos]);
-				}
+				instanceBuilder.addReservedStaticAddress(MacAddress.of(macIpSplit[macPos]), IPv4Address.of(macIpSplit[ipPos]));
+				log.info("Configured fixed address of {} for device {}", 
+						IPv4Address.of(macIpSplit[ipPos]).toString(), 
+						MacAddress.of(macIpSplit[macPos]).toString());
 			}
 		}
 
-		// The order of the DNS and NTP servers should be most reliable to least
+		/*
+		 * Separate the servers in the comma-delimited list,
+		 * otherwise the client will get incorrect option information
+		 */
 		String dnses = configOptions.get("domain-name-servers");
-		String ntps = configOptions.get("ntp-servers");
-
-		// Separate the servers in the comma-delimited list
-		// TODO If the list is null then we need to not include this information with the options request,
-		// otherwise the client will get incorrect option information
 		if (dnses != null) {
-			DHCP_SERVER_DNS_IP_LIST = IPv4.toIPv4AddressBytes(dnses.split("\\s*,\\s*")[0].toString());
+			List<IPv4Address> dnsIps = new ArrayList<IPv4Address>();
+			for (String dnsIp : dnses.split("\\s*,\\s*")) {
+				dnsIps.add(IPv4Address.of(dnsIp));
+			}
+			instanceBuilder.setDnsIps(dnsIps);
 		}
+
+		String ntps = configOptions.get("ntp-servers");
 		if (ntps != null) {
-			DHCP_SERVER_NTP_IP_LIST = IPv4.toIPv4AddressBytes(ntps.split("\\s*,\\s*")[0].toString());
+			List<IPv4Address> ntpIps = new ArrayList<IPv4Address>();
+			for (String ntpIp : ntps.split("\\s*,\\s*")) {
+				ntpIps.add(IPv4Address.of(ntpIp));
+			}
+			instanceBuilder.setNtpIps(ntpIps);
 		}
 
+		DHCPInstance instance = instanceBuilder.build();
+		instances.put(instance.getName(), instance);
+
 		// Monitor bindings for expired leases and clean them up
 		leasePoliceDispatcher = new ScheduledThreadPoolExecutor(1);
 		leasePolicePatrol = new DHCPLeasePolice();
-		/*leasePoliceOfficer = */
 		leasePoliceDispatcher.scheduleAtFixedRate(leasePolicePatrol, 10, 
 				DHCP_SERVER_LEASE_POLICE_PATROL_PERIOD_SECONDS, TimeUnit.SECONDS);
+
+		String enable = configOptions.get("enable");
+		if (enable != null && !enable.isEmpty() &&
+				(enable.toLowerCase().contains("true") ||
+						enable.toLowerCase().contains("yes") ||
+						enable.toLowerCase().contains("yep")
+						)
+				) {
+			enable();
+		} else {
+			disable();
+		}
 	}
 
 	@Override
 	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
-		return null;
+		Collection<Class<? extends IFloodlightService>> s = 
+				new HashSet<Class<? extends IFloodlightService>>();
+		s.add(IDHCPService.class);
+		return s;
 	}
 
 	@Override
 	public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
-		return null;
+		Map<Class<? extends IFloodlightService>, IFloodlightService> m = 
+				new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+		m.put(IDHCPService.class, this);
+		return m;
 	}
 
 	@Override
 	public String getName() {
-		return DHCPServer.class.getSimpleName();
+		return "dhcpserver";
 	}
 
 	@Override
@@ -342,7 +342,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		// We will rely on forwarding to forward out any DHCP packets that are not
 		// destined for our DHCP server. This is to allow an environment where
 		// multiple DHCP servers operate cooperatively
-		if (type == OFType.PACKET_IN && name.equals(Forwarding.class.getSimpleName())) {
+		if (type == OFType.PACKET_IN && name.equals("forwarding")) {
 			return true;
 		} else {
 			return false;
@@ -364,7 +364,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		return bytes;
 	}
 
-	public void sendDHCPOffer(IOFSwitch sw, OFPort inPort, MacAddress chaddr, IPv4Address dstIPAddr, 
+	public void sendDHCPOffer(DHCPInstance inst, IOFSwitch sw, OFPort inPort, MacAddress chaddr, IPv4Address dstIPAddr, 
 			IPv4Address yiaddr, IPv4Address giaddr, int xid, ArrayList<Byte> requestOrder) {
 		// Compose DHCP OFFER
 		/** (2) DHCP Offer
@@ -391,7 +391,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		DHCPOfferPacket.setBufferId(OFBufferId.NO_BUFFER);
 
 		Ethernet ethDHCPOffer = new Ethernet();
-		ethDHCPOffer.setSourceMACAddress(CONTROLLER_MAC);
+		ethDHCPOffer.setSourceMACAddress(inst.getServerMac());
 		ethDHCPOffer.setDestinationMACAddress(chaddr);
 		ethDHCPOffer.setEtherType(EthType.IPv4);
 
@@ -401,7 +401,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		} else { // Client has IP and dhcpc must have crashed
 			ipv4DHCPOffer.setDestinationAddress(dstIPAddr);
 		}
-		ipv4DHCPOffer.setSourceAddress(CONTROLLER_IP);
+		ipv4DHCPOffer.setSourceAddress(inst.getServerIp());
 		ipv4DHCPOffer.setProtocol(IpProtocol.UDP);
 		ipv4DHCPOffer.setTtl((byte) 64);
 
@@ -419,7 +419,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		dhcpDHCPOffer.setFlags((short) 0);
 		dhcpDHCPOffer.setClientIPAddress(UNASSIGNED_IP);
 		dhcpDHCPOffer.setYourIPAddress(yiaddr);
-		dhcpDHCPOffer.setServerIPAddress(CONTROLLER_IP);
+		dhcpDHCPOffer.setServerIPAddress(inst.getServerIp());
 		dhcpDHCPOffer.setGatewayIPAddress(giaddr);
 		dhcpDHCPOffer.setClientHardwareAddress(chaddr);
 
@@ -436,67 +436,85 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 			if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_SN) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_SN);
-				newOption.setData(DHCP_SERVER_SUBNET_MASK.getBytes());
+				newOption.setData(inst.getSubnetMask().getBytes());
 				newOption.setLength((byte) 4);
 				dhcpOfferOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_ROUTER) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_ROUTER);
-				newOption.setData(DHCP_SERVER_ROUTER_IP.getBytes());
+				newOption.setData(inst.getRouterIp().getBytes());
 				newOption.setLength((byte) 4);
 				dhcpOfferOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DN) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DN);
-				newOption.setData(DHCP_SERVER_DN);
-				newOption.setLength((byte) DHCP_SERVER_DN.length);
+				newOption.setData(inst.getDomainName().getBytes());
+				newOption.setLength((byte) inst.getDomainName().getBytes().length);
 				dhcpOfferOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DNS) {
-				newOption = new DHCPOption();
-				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DNS);
-				newOption.setData(DHCP_SERVER_DNS_IP_LIST);
-				newOption.setLength((byte) DHCP_SERVER_DNS_IP_LIST.length);
-				dhcpOfferOptions.add(newOption);
+				if (!inst.getDnsIps().isEmpty()) {
+					newOption = new DHCPOption();
+					newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DNS);
+					ByteBuffer bb = ByteBuffer.allocate(inst.getDnsIps().size() * 4 /* sizeof(IPv4Address) */); /* exact size */
+					for (IPv4Address ip : inst.getDnsIps()) {
+						bb.put(ip.getBytes());
+					}
+					bb.flip();
+					newOption.setData(bb.array()); //TODO verify this
+					newOption.setLength((byte) bb.array().length);
+					dhcpOfferOptions.add(newOption);
+				} else {
+					log.warn("Client asked for DNS servers, but we didn't have any for instance {}", inst.getName());
+				}
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP);
-				newOption.setData(DHCP_SERVER_BROADCAST_IP.getBytes());
+				newOption.setData(inst.getBroadcastIp().getBytes());
 				newOption.setLength((byte) 4);
 				dhcpOfferOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER);
-				newOption.setData(DHCP_SERVER_DHCP_SERVER_IP.getBytes());
+				newOption.setData(inst.getServerIp().getBytes());
 				newOption.setLength((byte) 4);
 				dhcpOfferOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME);
-				newOption.setData(intToBytes(DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS));
+				newOption.setData(intToBytes(inst.getLeaseTimeSec()));
 				newOption.setLength((byte) 4);
 				dhcpOfferOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_NTP_IP) {
-				newOption = new DHCPOption();
-				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_NTP_IP);
-				newOption.setData(DHCP_SERVER_NTP_IP_LIST);
-				newOption.setLength((byte) DHCP_SERVER_NTP_IP_LIST.length);
-				dhcpOfferOptions.add(newOption);
+				if (!inst.getNtpIps().isEmpty()) {
+					newOption = new DHCPOption();
+					newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_NTP_IP);
+					ByteBuffer bb = ByteBuffer.allocate(inst.getNtpIps().size() * 4); /* exact size */
+					for (IPv4Address ip : inst.getNtpIps()) {
+						bb.put(ip.getBytes());
+					}
+					bb.flip();
+					newOption.setData(bb.array()); //TODO verify this
+					newOption.setLength((byte) bb.array().length);
+					dhcpOfferOptions.add(newOption);
+				} else {
+					log.warn("Client asked for NTP servers, but we didn't have any for instance {}", inst.getName());
+				}
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME);
-				newOption.setData(intToBytes(DHCP_SERVER_REBIND_TIME_SECONDS));
+				newOption.setData(intToBytes(inst.getRebindTimeSec()));
 				newOption.setLength((byte) 4);
 				dhcpOfferOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME);
-				newOption.setData(intToBytes(DHCP_SERVER_RENEWAL_TIME_SECONDS));
+				newOption.setData(intToBytes(inst.getRenewalTimeSec()));
 				newOption.setLength((byte) 4);
 				dhcpOfferOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING);
-				newOption.setData(DHCP_SERVER_IP_FORWARDING);
+				newOption.setData(intToBytesSizeOne(inst.getIpForwarding() ? 1 : 0));
 				newOption.setLength((byte) 1);
 				dhcpOfferOptions.add(newOption);
 			} else {
@@ -525,7 +543,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		sw.write(DHCPOfferPacket.build());
 	}
 
-	public void sendDHCPAck(IOFSwitch sw, OFPort inPort, MacAddress chaddr, IPv4Address dstIPAddr, 
+	public void sendDHCPAck(DHCPInstance inst, IOFSwitch sw, OFPort inPort, MacAddress chaddr, IPv4Address dstIPAddr, 
 			IPv4Address yiaddr, IPv4Address giaddr, int xid, ArrayList<Byte> requestOrder) {
 		/** (4) DHCP ACK
 		 * -- UDP src port = 67
@@ -551,7 +569,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		DHCPACKPacket.setBufferId(OFBufferId.NO_BUFFER);
 
 		Ethernet ethDHCPAck = new Ethernet();
-		ethDHCPAck.setSourceMACAddress(CONTROLLER_MAC);
+		ethDHCPAck.setSourceMACAddress(inst.getServerMac());
 		ethDHCPAck.setDestinationMACAddress(chaddr);
 		ethDHCPAck.setEtherType(EthType.IPv4);
 
@@ -561,7 +579,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		} else { // Client has IP and dhclient must have crashed
 			ipv4DHCPAck.setDestinationAddress(dstIPAddr);
 		}
-		ipv4DHCPAck.setSourceAddress(CONTROLLER_IP);
+		ipv4DHCPAck.setSourceAddress(inst.getServerIp());
 		ipv4DHCPAck.setProtocol(IpProtocol.UDP);
 		ipv4DHCPAck.setTtl((byte) 64);
 
@@ -579,7 +597,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		dhcpDHCPAck.setFlags((short) 0);
 		dhcpDHCPAck.setClientIPAddress(UNASSIGNED_IP);
 		dhcpDHCPAck.setYourIPAddress(yiaddr);
-		dhcpDHCPAck.setServerIPAddress(CONTROLLER_IP);
+		dhcpDHCPAck.setServerIPAddress(inst.getServerIp());
 		dhcpDHCPAck.setGatewayIPAddress(giaddr);
 		dhcpDHCPAck.setClientHardwareAddress(chaddr);
 
@@ -596,67 +614,85 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 			if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_SN) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_SN);
-				newOption.setData(DHCP_SERVER_SUBNET_MASK.getBytes());
+				newOption.setData(inst.getSubnetMask().getBytes());
 				newOption.setLength((byte) 4);
 				dhcpAckOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_ROUTER) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_ROUTER);
-				newOption.setData(DHCP_SERVER_ROUTER_IP.getBytes());
+				newOption.setData(inst.getRouterIp().getBytes());
 				newOption.setLength((byte) 4);
 				dhcpAckOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DN) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DN);
-				newOption.setData(DHCP_SERVER_DN);
-				newOption.setLength((byte) DHCP_SERVER_DN.length);
+				newOption.setData(inst.getDomainName().getBytes());
+				newOption.setLength((byte) inst.getDomainName().getBytes().length);
 				dhcpAckOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DNS) {
-				newOption = new DHCPOption();
-				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DNS);
-				newOption.setData(DHCP_SERVER_DNS_IP_LIST);
-				newOption.setLength((byte) DHCP_SERVER_DNS_IP_LIST.length);
-				dhcpAckOptions.add(newOption);
+				if (!inst.getDnsIps().isEmpty()) {
+					newOption = new DHCPOption();
+					newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DNS);
+					ByteBuffer bb = ByteBuffer.allocate(inst.getDnsIps().size() * 4); /* exact size */
+					for (IPv4Address ip : inst.getDnsIps()) {
+						bb.put(ip.getBytes());
+					}
+					bb.flip();
+					newOption.setData(bb.array()); //TODO verify this
+					newOption.setLength((byte) bb.array().length);
+					dhcpAckOptions.add(newOption);
+				} else {
+					log.warn("Client asked for DNS servers, but we didn't have any for instance {}", inst.getName());
+				}
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP);
-				newOption.setData(DHCP_SERVER_BROADCAST_IP.getBytes());
+				newOption.setData(inst.getBroadcastIp().getBytes());
 				newOption.setLength((byte) 4);
 				dhcpAckOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER);
-				newOption.setData(DHCP_SERVER_DHCP_SERVER_IP.getBytes());
+				newOption.setData(inst.getServerIp().getBytes());
 				newOption.setLength((byte) 4);
 				dhcpAckOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME);
-				newOption.setData(intToBytes(DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS));
+				newOption.setData(intToBytes(inst.getLeaseTimeSec()));
 				newOption.setLength((byte) 4);
 				dhcpAckOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_NTP_IP) {
-				newOption = new DHCPOption();
-				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_NTP_IP);
-				newOption.setData(DHCP_SERVER_NTP_IP_LIST);
-				newOption.setLength((byte) DHCP_SERVER_NTP_IP_LIST.length);
-				dhcpAckOptions.add(newOption);
+				if (!inst.getNtpIps().isEmpty()) {
+					newOption = new DHCPOption();
+					newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_NTP_IP);
+					ByteBuffer bb = ByteBuffer.allocate(inst.getNtpIps().size() * 4); /* exact size */
+					for (IPv4Address ip : inst.getNtpIps()) {
+						bb.put(ip.getBytes());
+					}
+					bb.flip();
+					newOption.setData(bb.array()); //TODO verify this
+					newOption.setLength((byte) bb.array().length);
+					dhcpAckOptions.add(newOption);
+				} else {
+					log.warn("Client asked for NTP servers, but we didn't have any for instance {}", inst.getName());
+				}
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME);
-				newOption.setData(intToBytes(DHCP_SERVER_REBIND_TIME_SECONDS));
+				newOption.setData(intToBytes(inst.getRebindTimeSec()));
 				newOption.setLength((byte) 4);
 				dhcpAckOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME);
-				newOption.setData(intToBytes(DHCP_SERVER_RENEWAL_TIME_SECONDS));
+				newOption.setData(intToBytes(inst.getRenewalTimeSec()));
 				newOption.setLength((byte) 4);
 				dhcpAckOptions.add(newOption);
 			} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING) {
 				newOption = new DHCPOption();
 				newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING);
-				newOption.setData(DHCP_SERVER_IP_FORWARDING);
+				newOption.setData(intToBytesSizeOne(inst.getIpForwarding() ? 1 : 0));
 				newOption.setLength((byte) 1);
 				dhcpAckOptions.add(newOption);
 			}else {
@@ -685,18 +721,18 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		sw.write(DHCPACKPacket.build());
 	}
 
-	public void sendDHCPNack(IOFSwitch sw, OFPort inPort, MacAddress chaddr, IPv4Address giaddr, int xid) {
+	public void sendDHCPNack(DHCPInstance inst, IOFSwitch sw, OFPort inPort, MacAddress chaddr, IPv4Address giaddr, int xid) {
 		OFPacketOut.Builder DHCPOfferPacket = sw.getOFFactory().buildPacketOut();
 		DHCPOfferPacket.setBufferId(OFBufferId.NO_BUFFER);
 
 		Ethernet ethDHCPOffer = new Ethernet();
-		ethDHCPOffer.setSourceMACAddress(CONTROLLER_MAC);
+		ethDHCPOffer.setSourceMACAddress(inst.getServerMac());
 		ethDHCPOffer.setDestinationMACAddress(chaddr);
 		ethDHCPOffer.setEtherType(EthType.IPv4);
 
 		IPv4 ipv4DHCPOffer = new IPv4();
 		ipv4DHCPOffer.setDestinationAddress(BROADCAST_IP);
-		ipv4DHCPOffer.setSourceAddress(CONTROLLER_IP);
+		ipv4DHCPOffer.setSourceAddress(inst.getServerIp());
 		ipv4DHCPOffer.setProtocol(IpProtocol.UDP);
 		ipv4DHCPOffer.setTtl((byte) 64);
 
@@ -714,7 +750,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		dhcpDHCPOffer.setFlags((short) 0);
 		dhcpDHCPOffer.setClientIPAddress(UNASSIGNED_IP);
 		dhcpDHCPOffer.setYourIPAddress(UNASSIGNED_IP);
-		dhcpDHCPOffer.setServerIPAddress(CONTROLLER_IP);
+		dhcpDHCPOffer.setServerIPAddress(inst.getServerIp());
 		dhcpDHCPOffer.setGatewayIPAddress(giaddr);
 		dhcpDHCPOffer.setClientHardwareAddress(chaddr);
 
@@ -729,7 +765,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 
 		newOption = new DHCPOption();
 		newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER);
-		newOption.setData(DHCP_SERVER_DHCP_SERVER_IP.getBytes());
+		newOption.setData(inst.getServerIp().getBytes());
 		newOption.setLength((byte) 4);
 		dhcpOfferOptions.add(newOption);
 
@@ -791,7 +827,7 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 				//log.debug("Requested option 0x" + Byte.toString(specificRequest) + " not available");
 			}
 		}
-		
+
 		// We need to add these in regardless if the request list includes them
 		if (!isInform) {
 			if (!requestedLeaseTime) {
@@ -812,10 +848,21 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 
 	@Override
 	public net.floodlightcontroller.core.IListener.Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
-
 		OFPacketIn pi = (OFPacketIn) msg;
+		OFPort inPort = OFMessageUtils.getInPort(pi);
+		OFVlanVidMatch vlan = OFMessageUtils.getVlan(pi);
 
-		if (!theDHCPPool.hasAvailableAddresses()) {
+		DHCPInstance instance = getInstance(new NodePortTuple(sw.getId(), inPort));
+		if (instance == null) {
+			log.debug("Could not locate DHCP instance for DPID {}, port {}. Checking VLAN next", sw.getId(), inPort);
+			instance = getInstance(vlan.getVlanVid());
+		}
+		if (instance == null) {
+			log.error("Could not locate DHCP instance for DPID {}, port {}, VLAN {}", new Object[] { sw.getId(), inPort,  });
+			return Command.CONTINUE;
+		}
+
+		if (!instance.getPool().hasAvailableAddresses()) {
 			log.info("DHCP Pool is full! Consider increasing the pool size.");
 			return Command.CONTINUE;
 		}
@@ -835,12 +882,11 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 				if ((UDPPayload.getDestinationPort().equals(UDP.DHCP_SERVER_PORT) /* TransportPort must be deep though */
 						|| UDPPayload.getDestinationPort().equals(UDP.DHCP_CLIENT_PORT))
 						&& (UDPPayload.getSourcePort().equals(UDP.DHCP_SERVER_PORT)
-						|| UDPPayload.getSourcePort().equals(UDP.DHCP_CLIENT_PORT)))
+								|| UDPPayload.getSourcePort().equals(UDP.DHCP_CLIENT_PORT)))
 				{
 					log.debug("Got DHCP Packet");
 					// This is a DHCP packet that we need to process
 					DHCP DHCPPayload = (DHCP) UDPPayload.getPayload();
-					OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
 
 					/* DHCP/IPv4 Header Information */
 					int xid = 0;
@@ -893,29 +939,29 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 							// Process DISCOVER message and prepare an OFFER with minimum-hold lease
 							// A HOLD lease should be a small amount of time sufficient for the client to respond
 							// with a REQUEST, at which point the ACK will set the least time to the DEFAULT
-							synchronized (theDHCPPool) {
-								if (!theDHCPPool.hasAvailableAddresses()) {
+							synchronized (instance.getPool()) {
+								if (!instance.getPool().hasAvailableAddresses()) {
 									log.info("DHCP Pool is full! Consider increasing the pool size.");
 									log.info("Device with MAC " + chaddr.toString() + " was not granted an IP lease");
 									return Command.CONTINUE;
 								}
-								DHCPBinding lease = theDHCPPool.getSpecificAvailableLease(desiredIPAddr, chaddr);
+								DHCPBinding lease = instance.getPool().getSpecificAvailableLease(desiredIPAddr, chaddr);
 
 								if (lease != null) {
 									log.debug("Checking new lease with specific IP");
-									theDHCPPool.setDHCPbinding(lease, chaddr, DHCP_SERVER_HOLD_LEASE_TIME_SECONDS);
+									instance.getPool().setDHCPbinding(lease, chaddr, instance.getHoldTimeSec());
 									yiaddr = lease.getIPv4Address();
 									log.debug("Got new lease for " + yiaddr.toString());
 								} else {
 									log.debug("Checking new lease for any IP");
-									lease = theDHCPPool.getAnyAvailableLease(chaddr);
-									theDHCPPool.setDHCPbinding(lease, chaddr, DHCP_SERVER_HOLD_LEASE_TIME_SECONDS);
+									lease = instance.getPool().getAnyAvailableLease(chaddr);
+									instance.getPool().setDHCPbinding(lease, chaddr, instance.getHoldTimeSec());
 									yiaddr = lease.getIPv4Address();
 									log.debug("Got new lease for " + yiaddr.toString());
 								}
 							}
 
-							sendDHCPOffer(sw, inPort, chaddr, IPv4SrcAddr, yiaddr, giaddr, xid, requestOrder);
+							sendDHCPOffer(instance, sw, inPort, chaddr, IPv4SrcAddr, yiaddr, giaddr, xid, requestOrder);
 						} // END IF DISCOVER
 
 						/** (3) DHCP Request
@@ -947,15 +993,15 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 							for (DHCPOption option : options) {
 								if (option.getCode() == DHCP_REQ_PARAM_OPTION_CODE_REQUESTED_IP) {
 									desiredIPAddr = IPv4Address.of(option.getData());
-									if (!desiredIPAddr.equals(theDHCPPool.getDHCPbindingFromMAC(chaddr).getIPv4Address())) {
+									if (!desiredIPAddr.equals(instance.getPool().getDHCPbindingFromMAC(chaddr).getIPv4Address())) {
 										// This client wants a different IP than what we have on file, so cancel its HOLD lease now (if we have one)
-										theDHCPPool.cancelLeaseOfMAC(chaddr);
+										instance.getPool().cancelLeaseOfMAC(chaddr);
 										return Command.CONTINUE;
 									}
 								} else if (option.getCode() == DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER) {
-									if (!IPv4Address.of(option.getData()).equals(DHCP_SERVER_DHCP_SERVER_IP)) {
+									if (!IPv4Address.of(option.getData()).equals(instance.getServerIp())) {
 										// We're not the DHCPServer the client wants to use, so cancel its HOLD lease now and ignore the client
-										theDHCPPool.cancelLeaseOfMAC(chaddr);
+										instance.getPool().cancelLeaseOfMAC(chaddr);
 										return Command.CONTINUE;
 									}
 								} else if (option.getCode() == DHCP_REQ_PARAM_OPTION_CODE_REQUESTED_PARAMTERS) {
@@ -965,8 +1011,8 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 							// Process REQUEST message and prepare an ACK with default lease time
 							// This extends the hold lease time to that of a normal lease
 							boolean sendACK = true;
-							synchronized (theDHCPPool) {
-								if (!theDHCPPool.hasAvailableAddresses()) {
+							synchronized (instance.getPool()) {
+								if (!instance.getPool().hasAvailableAddresses()) {
 									log.info("DHCP Pool is full! Consider increasing the pool size.");
 									log.info("Device with MAC " + chaddr.toString() + " was not granted an IP lease");
 									return Command.CONTINUE;
@@ -974,14 +1020,14 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 								DHCPBinding lease;
 								// Get any binding, in use now or not
 								if (desiredIPAddr != null) {
-									lease = theDHCPPool.getDHCPbindingFromIPv4(desiredIPAddr);
+									lease = instance.getPool().getDHCPbindingFromIPv4(desiredIPAddr);
 								} else {
-									lease = theDHCPPool.getAnyAvailableLease(chaddr);
+									lease = instance.getPool().getAnyAvailableLease(chaddr);
 								}
 								// This IP is not in our allocation range
 								if (lease == null) {
 									log.info("The IP " + desiredIPAddr.toString() + " is not in the range " 
-											+ DHCP_SERVER_IP_START.toString() + " to " + DHCP_SERVER_IP_STOP.toString());
+											+ instance.getPool().getStartIp().toString() + "+");
 									log.info("Device with MAC " + chaddr.toString() + " was not granted an IP lease");
 									sendACK = false;
 									// Determine if the IP in the binding we just retrieved is okay to allocate to the MAC requesting it
@@ -992,13 +1038,13 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 									// Check if we want to renew the MAC's current lease
 								} else if (lease.getMACAddress().equals(chaddr) && lease.isActiveLease()) {
 									log.debug("Renewing lease for MAC " + chaddr.toString());
-									theDHCPPool.renewLease(lease.getIPv4Address(), DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS);
+									instance.getPool().renewLease(lease.getIPv4Address(), instance.getLeaseTimeSec());
 									yiaddr = lease.getIPv4Address();
 									log.debug("Finalized renewed lease for " + yiaddr.toString());
 									// Check if we want to create a new lease for the MAC
 								} else if (!lease.isActiveLease()){
 									log.debug("Assigning new lease for MAC " + chaddr.toString());
-									theDHCPPool.setDHCPbinding(lease, chaddr, DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS);
+									instance.getPool().setDHCPbinding(lease, chaddr, instance.getLeaseTimeSec());
 									yiaddr = lease.getIPv4Address();
 									log.debug("Finalized new lease for " + yiaddr.toString());
 								} else {
@@ -1007,22 +1053,22 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 								}
 							}
 							if (sendACK) {
-								sendDHCPAck(sw, inPort, chaddr, IPv4SrcAddr, yiaddr, giaddr, xid, requestOrder);							
+								sendDHCPAck(instance, sw, inPort, chaddr, IPv4SrcAddr, yiaddr, giaddr, xid, requestOrder);							
 							} else {
-								sendDHCPNack(sw, inPort, chaddr, giaddr, xid);
+								sendDHCPNack(instance, sw, inPort, chaddr, giaddr, xid);
 							}
 						} // END IF REQUEST
 						else if (Arrays.equals(DHCPPayload.getOption(DHCP.DHCPOptionCode.OptionCode_MessageType).getData(), DHCP_MSG_TYPE_RELEASE)) {
-							if (DHCPPayload.getServerIPAddress() != CONTROLLER_IP) {
+							if (!DHCPPayload.getServerIPAddress().equals(instance.getServerIp())) {
 								log.info("DHCP RELEASE message not for our DHCP server");
 								// Send the packet out the port it would normally go out via the Forwarding module
 								// Execution jumps to return Command.CONTINUE at end of receive()
 							} else {
 								log.debug("Got DHCP RELEASE. Cancelling remaining time on DHCP lease");
-								synchronized(theDHCPPool) {
-									if (theDHCPPool.cancelLeaseOfMAC(DHCPPayload.getClientHardwareAddress())) {
+								synchronized(instance.getPool()) {
+									if (instance.getPool().cancelLeaseOfMAC(DHCPPayload.getClientHardwareAddress())) {
 										log.info("Cancelled DHCP lease of " + DHCPPayload.getClientHardwareAddress().toString());
-										log.info("IP " + theDHCPPool.getDHCPbindingFromMAC(DHCPPayload.getClientHardwareAddress()).getIPv4Address().toString()
+										log.info("IP " + instance.getPool().getDHCPbindingFromMAC(DHCPPayload.getClientHardwareAddress()).getIPv4Address().toString()
 												+ " is now available in the DHCP address pool");
 									} else {
 										log.debug("Lease of " + DHCPPayload.getClientHardwareAddress().toString()
@@ -1033,10 +1079,10 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 						} // END IF RELEASE
 						else if (Arrays.equals(DHCPPayload.getOption(DHCP.DHCPOptionCode.OptionCode_MessageType).getData(), DHCP_MSG_TYPE_DECLINE)) {
 							log.debug("Got DHCP DECLINE. Cancelling HOLD time on DHCP lease");
-							synchronized(theDHCPPool) {
-								if (theDHCPPool.cancelLeaseOfMAC(DHCPPayload.getClientHardwareAddress())) {
+							synchronized(instance.getPool()) {
+								if (instance.getPool().cancelLeaseOfMAC(DHCPPayload.getClientHardwareAddress())) {
 									log.info("Cancelled DHCP lease of " + DHCPPayload.getClientHardwareAddress().toString());
-									log.info("IP " + theDHCPPool.getDHCPbindingFromMAC(DHCPPayload.getClientHardwareAddress()).getIPv4Address().toString()
+									log.info("IP " + instance.getPool().getDHCPbindingFromMAC(DHCPPayload.getClientHardwareAddress()).getIPv4Address().toString()
 											+ " is now available in the DHCP address pool");
 								} else {
 									log.info("HOLD Lease of " + DHCPPayload.getClientHardwareAddress().toString()
@@ -1054,9 +1100,9 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 
 							// Get the requests from the INFORM message. True for inform -- we don't want to include lease information
 							requestOrder = getRequestedParameters(DHCPPayload, true);
-							
+
 							// Process INFORM message and send an ACK with requested information
-							sendDHCPAck(sw, inPort, chaddr, IPv4SrcAddr, yiaddr, giaddr, xid, requestOrder);							
+							sendDHCPAck(instance, sw, inPort, chaddr, IPv4SrcAddr, yiaddr, giaddr, xid, requestOrder);							
 						} // END IF INFORM
 					} // END IF DHCP OPCODE REQUEST 
 					else if (DHCPPayload.getOpCode() == DHCP_OPCODE_REPLY) {
@@ -1095,15 +1141,86 @@ public class DHCPServer implements IOFMessageListener, IFloodlightModule  {
 		public void run() {
 			log.info("Cleaning any expired DHCP leases...");
 			ArrayList<DHCPBinding> newAvailableBindings;
-			synchronized(theDHCPPool) {
-				// Loop through lease pool and check all leases to see if they are expired
-				// If a lease is expired, then clean it up and make the binding available
-				newAvailableBindings = theDHCPPool.cleanExpiredLeases();
-			}
-			for (DHCPBinding binding : newAvailableBindings) {
-				log.info("MAC " + binding.getMACAddress().toString() + " has expired");
-				log.info("Lease now available for IP " + binding.getIPv4Address().toString());
+			for (DHCPInstance instance : instances.values()) {
+				synchronized(instance.getPool()) {
+					// Loop through lease pool and check all leases to see if they are expired
+					// If a lease is expired, then clean it up and make the binding available
+					newAvailableBindings = instance.getPool().cleanExpiredLeases();
+				}
+				for (DHCPBinding binding : newAvailableBindings) {
+					log.info("MAC " + binding.getMACAddress().toString() + " has expired");
+					log.info("Lease now available for IP " + binding.getIPv4Address().toString());
+				}
 			}
 		}
 	} // END DHCPLeasePolice Class
+
+	@Override
+	public synchronized void enable() {
+		log.warn("DHCP server module enabled");
+		enabled = true;
+	}
+
+	@Override
+	public synchronized void disable() {
+		log.warn("DHCP server module disabled");
+		enabled = false;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return enabled;
+	}
+
+	@Override
+	public synchronized boolean addInstance(DHCPInstance instance) {
+		if (instances.containsKey(instance.getName())) {
+			log.error("DHCP instance {} already present. Not adding", instance.getName());
+			return false;
+		} else {
+			instances.put(instance.getName(), instance);
+			return true;
+		}
+	}
+
+	@Override
+	public DHCPInstance getInstance(String name) {
+		return instances.get(name);
+	}
+
+	@Override
+	public DHCPInstance getInstance(NodePortTuple member) {
+		for (DHCPInstance instance : instances.values()) {
+			if (instance.getMemberPorts().contains(member)) {
+				return instance;
+			}
+		}
+		return null;
+	}
+
+	@Override
+	public DHCPInstance getInstance(VlanVid member) {
+		for (DHCPInstance instance : instances.values()) {
+			if (instance.getMemberVlans().contains(member)) {
+				return instance;
+			}
+		}
+		return null;
+	}
+
+	@Override
+	public Collection<DHCPInstance> getInstances() {
+		return Collections.unmodifiableCollection(instances.values());
+	}
+
+	@Override
+	public synchronized boolean deleteInstance(String name) {
+		if (instances.containsKey(name)) {
+			log.error("DHCP instance {} not present. Cannot delete", name);
+			return false;
+		} else {
+			instances.remove(name);
+			return true;
+		}
+	}
 } // END DHCPServer Class
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/dhcpserver/DHCPSwitchFlowSetter.java b/src/main/java/net/floodlightcontroller/dhcpserver/DHCPSwitchFlowSetter.java
index bf4490de34582fc3f6762db977414736da7a5297..986400c7bbbe6ca7ab79d7921c221a9860a5a5e8 100644
--- a/src/main/java/net/floodlightcontroller/dhcpserver/DHCPSwitchFlowSetter.java
+++ b/src/main/java/net/floodlightcontroller/dhcpserver/DHCPSwitchFlowSetter.java
@@ -28,12 +28,12 @@ import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.packet.UDP;
-import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
+import net.floodlightcontroller.staticentry.IStaticEntryPusherService;
 // Adding a comment to test a new commit
 public class DHCPSwitchFlowSetter implements IFloodlightModule, IOFSwitchListener {
 	protected static Logger log;
 	protected IFloodlightProviderService floodlightProvider;
-	protected IStaticFlowEntryPusherService sfp;
+	protected IStaticEntryPusherService sfp;
 	protected IOFSwitchService switchService;
 	
 	@Override
@@ -51,7 +51,7 @@ public class DHCPSwitchFlowSetter implements IFloodlightModule, IOFSwitchListene
 		Collection<Class<? extends IFloodlightService>> l = 
 				new ArrayList<Class<? extends IFloodlightService>>();
 		l.add(IFloodlightProviderService.class);
-		l.add(IStaticFlowEntryPusherService.class);
+		l.add(IStaticEntryPusherService.class);
 		l.add(IOFSwitchService.class);
 		return l;
 	}
@@ -61,7 +61,7 @@ public class DHCPSwitchFlowSetter implements IFloodlightModule, IOFSwitchListene
 			throws FloodlightModuleException {
 		floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
 		log = LoggerFactory.getLogger(DHCPServer.class);
-		sfp = context.getServiceImpl(IStaticFlowEntryPusherService.class);
+		sfp = context.getServiceImpl(IStaticEntryPusherService.class);
 		switchService = context.getServiceImpl(IOFSwitchService.class);
 	}
 
diff --git a/src/main/java/net/floodlightcontroller/dhcpserver/IDHCPService.java b/src/main/java/net/floodlightcontroller/dhcpserver/IDHCPService.java
new file mode 100644
index 0000000000000000000000000000000000000000..6600d34d462e12c27f2629a6ff068eba6e3a699b
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/dhcpserver/IDHCPService.java
@@ -0,0 +1,24 @@
+package net.floodlightcontroller.dhcpserver;
+
+import java.util.Collection;
+
+import org.projectfloodlight.openflow.types.VlanVid;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.core.types.NodePortTuple;
+
+public interface IDHCPService extends IFloodlightService {
+
+	public void enable();
+	public void disable();
+	public boolean isEnabled();
+	
+	public boolean addInstance(DHCPInstance instance);
+	
+	public DHCPInstance getInstance(String name);
+	public DHCPInstance getInstance(NodePortTuple member);
+	public DHCPInstance getInstance(VlanVid member);
+	public Collection<DHCPInstance> getInstances();
+	
+	public boolean deleteInstance(String name);
+}
diff --git a/src/main/java/net/floodlightcontroller/dhcpserver/web/BindingResource.java b/src/main/java/net/floodlightcontroller/dhcpserver/web/BindingResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..07af88b2b2dc537efe391e4c97018e5bde969118
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/dhcpserver/web/BindingResource.java
@@ -0,0 +1,36 @@
+package net.floodlightcontroller.dhcpserver.web;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.floodlightcontroller.dhcpserver.IDHCPService;
+
+import org.restlet.resource.Delete;
+import org.restlet.resource.Post;
+import org.restlet.resource.Put;
+import org.restlet.resource.ServerResource;
+
+public class BindingResource extends ServerResource {
+
+	@Put
+	@Post
+	Map<String, String> addStaticBinding() {
+		IDHCPService dhcp = (IDHCPService) getContext().getAttributes().get(IDHCPService.class.getCanonicalName());
+        String whichInstance = (String) getRequestAttributes().get(DHCPServerWebRoutable.STR_INSTANCE);
+        Map<String, String> rc = new HashMap<String, String>(1);
+		
+		rc.put("result", "DHCP static binding added for instance " + whichInstance);
+		return rc;
+	}
+	
+	@Delete
+	Map<String, String> deleteStaticBinding() {
+		IDHCPService dhcp = (IDHCPService) getContext().getAttributes().get(IDHCPService.class.getCanonicalName());
+        String whichInstance = (String) getRequestAttributes().get(DHCPServerWebRoutable.STR_INSTANCE);
+		Map<String, String> rc = new HashMap<String, String>(1);
+		
+		rc.put("result", "DHCP static binding deleted for instance " + whichInstance);
+		return rc;
+	}
+	
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/dhcpserver/web/DHCPInstanceDeserializer.java b/src/main/java/net/floodlightcontroller/dhcpserver/web/DHCPInstanceDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e362104f6af141216eff28815e0df5bdd6b6c65
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/dhcpserver/web/DHCPInstanceDeserializer.java
@@ -0,0 +1,20 @@
+package net.floodlightcontroller.dhcpserver.web;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.dhcpserver.DHCPInstance;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+public class DHCPInstanceDeserializer extends JsonDeserializer<DHCPInstance> {
+
+	@Override
+	public DHCPInstance deserialize(JsonParser jParser, DeserializationContext dserCntx)
+			throws IOException, JsonProcessingException {
+		
+		return null;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/dhcpserver/web/DHCPInstanceSerializer.java b/src/main/java/net/floodlightcontroller/dhcpserver/web/DHCPInstanceSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..53d15f7bd1b210397f2303aa1b519bd38d09b2fd
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/dhcpserver/web/DHCPInstanceSerializer.java
@@ -0,0 +1,62 @@
+package net.floodlightcontroller.dhcpserver.web;
+
+import java.io.IOException;
+
+import org.projectfloodlight.openflow.types.IPv4Address;
+
+import net.floodlightcontroller.core.types.NodePortTuple;
+import net.floodlightcontroller.dhcpserver.DHCPInstance;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonGenerator.Feature;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public class DHCPInstanceSerializer extends JsonSerializer<DHCPInstance> {
+
+    @Override
+    public void serialize(DHCPInstance instance, JsonGenerator jGen,
+                          SerializerProvider serializer)
+                                  throws IOException, JsonProcessingException {
+    	jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);
+    	
+        jGen.writeStartObject();
+        jGen.writeStringField("name", instance.getName());
+        jGen.writeStringField("domain_name", instance.getDomainName());
+        jGen.writeStringField("broadcast_ip", instance.getBroadcastIp().toString());
+        
+        jGen.writeArrayFieldStart("dns_ips");
+        for (IPv4Address ip : instance.getDnsIps()) {
+            jGen.writeString(ip.toString());
+        }
+        jGen.writeEndArray();
+        
+        jGen.writeArrayFieldStart("ntp_ips");
+        for (IPv4Address ip : instance.getNtpIps()) {
+            jGen.writeString(ip.toString());
+        }
+        jGen.writeEndArray();
+        
+        jGen.writeArrayFieldStart("ntp_ips");
+        for (NodePortTuple ip : instance.getMemberPorts()) {
+        	jGen.writeStartObject();
+            jGen.writeString(ip.toString());
+        }
+        jGen.writeEndArray();
+
+        jGen.writeNumberField("lease_hold_time_sec", instance.getHoldTimeSec());
+        jGen.writeNumberField("lease_time_sec", instance.getLeaseTimeSec());
+        jGen.writeNumberField("lease_rebind_time_sec", instance.getRebindTimeSec());
+        jGen.writeNumberField("lease_renewal_time_sec", instance.getRenewalTimeSec());
+
+        jGen.writeBooleanField("ip_forwarding", instance.getIpForwarding());
+        jGen.writeStringField("router_ip", instance.getRouterIp().toString());
+        jGen.writeStringField("server_ip", instance.getServerIp().toString());
+        jGen.writeStringField("server_mac", instance.getServerMac().toString());
+        jGen.writeStringField("server_ip", instance.getSubnetMask().toString());
+
+        
+        jGen.writeEndObject();
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/dhcpserver/web/DHCPServerWebRoutable.java b/src/main/java/net/floodlightcontroller/dhcpserver/web/DHCPServerWebRoutable.java
new file mode 100644
index 0000000000000000000000000000000000000000..64cf3b59253dc10d2eabf58f53ea31d6b04e95ec
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/dhcpserver/web/DHCPServerWebRoutable.java
@@ -0,0 +1,33 @@
+package net.floodlightcontroller.dhcpserver.web;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.routing.Router;
+
+public class DHCPServerWebRoutable implements RestletRoutable {
+	protected static final String STR_INSTANCE = "instance";
+	protected static final String STR_ALL = "all";
+	
+    /**
+     * Create the Restlet router and bind to the proper resources.
+     */
+    @Override
+    public Router getRestlet(Context context) {
+        Router router = new Router(context);
+        router.attach("/add/instance/json", InstanceResource.class);
+        router.attach("/get/instance/{" + STR_INSTANCE + "}/json", InstanceResource.class);
+        router.attach("/del/instance/{" + STR_INSTANCE + "}/json", InstanceResource.class);
+        router.attach("/add/static-binding/json", BindingResource.class);
+
+        return router;
+    }
+
+    /**
+     * Set the base path for the DHCP Server
+     */
+    @Override
+    public String basePath() {
+        return "/wm/dhcpserver";
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/dhcpserver/web/InstanceResource.java b/src/main/java/net/floodlightcontroller/dhcpserver/web/InstanceResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..6349020f94d8768eb5cda14ba0f182ea102be776
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/dhcpserver/web/InstanceResource.java
@@ -0,0 +1,63 @@
+package net.floodlightcontroller.dhcpserver.web;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.floodlightcontroller.dhcpserver.DHCPInstance;
+import net.floodlightcontroller.dhcpserver.IDHCPService;
+
+import org.restlet.resource.Delete;
+import org.restlet.resource.Get;
+import org.restlet.resource.Post;
+import org.restlet.resource.Put;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InstanceResource extends ServerResource {
+	private static final Logger log = LoggerFactory.getLogger(InstanceResource.class);
+	
+	@Get
+	Collection<DHCPInstance> getInstance() {
+		IDHCPService dhcp = (IDHCPService) getContext().getAttributes().get(IDHCPService.class.getCanonicalName());
+        String whichInstance = (String) getRequestAttributes().get(DHCPServerWebRoutable.STR_INSTANCE);
+        Collection<DHCPInstance> instances = null;
+        
+        if (whichInstance == null || whichInstance.isEmpty() || whichInstance.equalsIgnoreCase(DHCPServerWebRoutable.STR_ALL)) {
+        	instances = dhcp.getInstances();
+        } else {
+        	DHCPInstance instance = dhcp.getInstance(whichInstance);
+        	if (instance != null) {
+        		instances = Collections.singleton(instance);
+        	}
+        }
+        
+        if (instances == null) {
+        	log.error("Could not locate DHCP instance {}", whichInstance);
+        	return Collections.emptySet();
+        }
+        return instances;
+	}
+	
+	@Put
+	@Post
+	Map<String, String> addInstance(String json) {
+		IDHCPService dhcp = (IDHCPService) getContext().getAttributes().get(IDHCPService.class.getCanonicalName());
+		Map<String, String> rc = new HashMap<String, String>(1);
+		
+		rc.put("result", "DHCP instance added");
+		return rc;
+	}
+	
+	@Delete
+	Map<String, String> delInstance() {
+		IDHCPService dhcp = (IDHCPService) getContext().getAttributes().get(IDHCPService.class.getCanonicalName());
+        String whichInstance = (String) getRequestAttributes().get(DHCPServerWebRoutable.STR_INSTANCE);
+		Map<String, String> rc = new HashMap<String, String>(1);
+		
+		rc.put("result", "DHCP instance " + whichInstance + " deleted");
+		return rc;
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java b/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java
index 5948c2491b864dca95c6e947dc0b946cd80550d9..27e4cfb87a22cbc3052fb46b4b16e11ad9c37bbc 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileManager.java
@@ -37,7 +37,6 @@ import net.floodlightcontroller.core.util.SingletonTask;
 import net.floodlightcontroller.debugcounter.DebugCounterResource;
 import net.floodlightcontroller.debugcounter.IDebugCounter;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
-import net.floodlightcontroller.flowcache.FlowReconcileQuery.FlowReconcileQueryDebugEvent;
 import net.floodlightcontroller.flowcache.IFlowReconcileListener;
 import net.floodlightcontroller.flowcache.OFMatchReconcile;
 import net.floodlightcontroller.flowcache.PriorityPendingQueue.EventPriority;
@@ -314,15 +313,6 @@ public class FlowReconcileManager implements IFloodlightModule, IFlowReconcileSe
 					break;
 				}
 			}
-			for (OFMatchReconcile ofmRc : ofmRcList) {
-				if (ofmRc.origReconcileQueryEvent != null) {
-					ofmRc.origReconcileQueryEvent.evType.getDebugEvent()
-					.newEventWithFlush(new FlowReconcileQueryDebugEvent(
-							ofmRc.origReconcileQueryEvent,
-							"Flow Reconciliation Complete",
-							ofmRc));
-				}
-			}
 			// Flush the flowCache counters.
 			updateFlush();
 			flowReconcileThreadRunCount.incrementAndGet();
diff --git a/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileQuery.java b/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileQuery.java
index 2235e181aea51b0b6d351edd788563570c4ab391..2e0a2e02ea161ca70ca84e2137415d8e953f4f2d 100644
--- a/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileQuery.java
+++ b/src/main/java/net/floodlightcontroller/flowcache/FlowReconcileQuery.java
@@ -16,14 +16,6 @@
 
 package net.floodlightcontroller.flowcache;
 
-import java.lang.ref.SoftReference;
-
-import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventType;
-import net.floodlightcontroller.debugevent.IEventCategory;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.flowcache.PriorityPendingQueue.EventPriority;
 
 /**
@@ -33,29 +25,7 @@ import net.floodlightcontroller.flowcache.PriorityPendingQueue.EventPriority;
 public class FlowReconcileQuery {
     public ReconcileQueryEvType evType;
     public EventPriority evPriority;
-    public static class FlowReconcileQueryDebugEvent {
-        @EventColumn(name = "Event Info",
-                     description = EventFieldType.SREF_OBJECT)
-        private final SoftReference<FlowReconcileQuery> eventInfo;
-        @EventColumn(name = "Stage",
-                     description = EventFieldType.STRING)
-        private final String stage;
-        @EventColumn(name = "Stage Info",
-                     description = EventFieldType.SREF_OBJECT)
-        private final SoftReference<Object> stageInfo;
-        public FlowReconcileQueryDebugEvent(FlowReconcileQuery eventInfo,
-                                            String stage,
-                                            Object stageInfo) {
-            super();
-            this.eventInfo = new SoftReference<FlowReconcileQuery>(eventInfo);
-            this.stage = stage;
-            if (stageInfo != null) {
-                this.stageInfo = new SoftReference<Object>(stageInfo);
-            } else {
-                this.stageInfo = null;
-            }
-        }
-    }
+    
     public static enum ReconcileQueryEvType {
         /* Interface rule of a bvs was modified */
         BVS_INTERFACE_RULE_CHANGED(EventPriority.LOW,
@@ -94,8 +64,6 @@ public class FlowReconcileQuery {
 
         private String description;
         private EventPriority priority;
-        private IEventCategory<FlowReconcileQueryDebugEvent> eventCategory;
-        private IDebugEventService debugEventService;
 
         private ReconcileQueryEvType(EventPriority priority, String description) {
             this.priority = priority;
@@ -109,23 +77,6 @@ public class FlowReconcileQuery {
         public String getDescription() {
             return description;
         }
-        
-        public void registerDebugEvent(String packageName, IDebugEventService debugEvents) {
-        	 if (debugEventService == null) {
-                 debugEventService = new MockDebugEventService();
-             }
-                eventCategory = debugEventService.buildEvent(FlowReconcileQueryDebugEvent.class)
-                    .setModuleName(packageName)
-                    .setEventName(this.toString().toLowerCase().replace("_", "-"))
-                    .setEventDescription(this.getDescription())
-                    .setEventType(EventType.ALWAYS_LOG)
-                    .setBufferCapacity(500)
-                    .register();
-        }
-    
-        public IEventCategory<FlowReconcileQueryDebugEvent> getDebugEvent() {
-            return eventCategory;
-        }
     }
     public FlowReconcileQuery(ReconcileQueryEvType evType) {
         this.evType = evType;
diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
index 955f453a2b5157432838debf5b86b13f744afe8c..25765b3632e88f8d2da25f824ae27fb0ca1ccfe8 100644
--- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
+++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
@@ -17,7 +17,6 @@
 
 package net.floodlightcontroller.forwarding;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -56,8 +55,10 @@ import net.floodlightcontroller.routing.Route;
 import net.floodlightcontroller.topology.ITopologyService;
 import net.floodlightcontroller.util.FlowModUtils;
 import net.floodlightcontroller.util.OFDPAUtils;
+import net.floodlightcontroller.util.OFMessageUtils;
 import net.floodlightcontroller.util.OFPortMode;
 import net.floodlightcontroller.util.OFPortModeTuple;
+import net.floodlightcontroller.util.ParseUtils;
 
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowModCommand;
@@ -87,572 +88,575 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class Forwarding extends ForwardingBase implements IFloodlightModule, IOFSwitchListener, ILinkDiscoveryListener {
-	protected static Logger log = LoggerFactory.getLogger(Forwarding.class);
-
-	@Override
-	public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
-		Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
-		// We found a routing decision (i.e. Firewall is enabled... it's the only thing that makes RoutingDecisions)
-		if (decision != null) {
-			if (log.isTraceEnabled()) {
-				log.trace("Forwarding decision={} was made for PacketIn={}", decision.getRoutingAction().toString(), pi);
-			}
-
-			switch(decision.getRoutingAction()) {
-			case NONE:
-				// don't do anything
-				return Command.CONTINUE;
-			case FORWARD_OR_FLOOD:
-			case FORWARD:
-				doForwardFlow(sw, pi, cntx, false);
-				return Command.CONTINUE;
-			case MULTICAST:
-				// treat as broadcast
-				doFlood(sw, pi, cntx);
-				return Command.CONTINUE;
-			case DROP:
-				doDropFlow(sw, pi, decision, cntx);
-				return Command.CONTINUE;
-			default:
-				log.error("Unexpected decision made for this packet-in={}", pi, decision.getRoutingAction());
-				return Command.CONTINUE;
-			}
-		} else { // No routing decision was found. Forward to destination or flood if bcast or mcast.
-			if (log.isTraceEnabled()) {
-				log.trace("No decision was made for PacketIn={}, forwarding", pi);
-			}
-
-			if (eth.isBroadcast() || eth.isMulticast()) {
-				doFlood(sw, pi, cntx);
-			} else {
-				doForwardFlow(sw, pi, cntx, false);
-			}
-		}
-
-		return Command.CONTINUE;
-	}
-
-	protected void doDropFlow(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
-		OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
-		Match m = createMatchFromPacket(sw, inPort, cntx);
-		OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd(); // this will be a drop-flow; a flow that will not output to any ports
-		List<OFAction> actions = new ArrayList<OFAction>(); // set no action to drop
-		U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
-		log.info("Droppingggg");
-		fmb.setCookie(cookie)
-		.setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
-		.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
-		.setBufferId(OFBufferId.NO_BUFFER)
-		.setMatch(m)
-		.setPriority(FLOWMOD_DEFAULT_PRIORITY);
-
-		FlowModUtils.setActions(fmb, actions, sw);
-
-		try {
-			if (log.isDebugEnabled()) {
-				log.debug("write drop flow-mod sw={} match={} flow-mod={}",
-						new Object[] { sw, m, fmb.build() });
-			}
-			boolean dampened = messageDamper.write(sw, fmb.build());
-			log.debug("OFMessage dampened: {}", dampened);
-		} catch (IOException e) {
-			log.error("Failure writing drop flow mod", e);
-		}
-	}
-
-	protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx, boolean requestFlowRemovedNotifn) {
-		OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
-		IDevice dstDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE);
-		DatapathId source = sw.getId();
-
-		if (dstDevice != null) {
-			IDevice srcDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE);
-
-			if (srcDevice == null) {
-				log.error("No device entry found for source device. Is the device manager running? If so, report bug.");
-				return;
-			}
-
-			if (FLOOD_ALL_ARP_PACKETS && 
-					IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD).getEtherType() 
-					== EthType.ARP) {
-				log.debug("ARP flows disabled in Forwarding. Flooding ARP packet");
-				doFlood(sw, pi, cntx);
-				return;
-			}
-
-			/* Validate that the source and destination are not on the same switch port */
-			boolean on_same_if = false;
-			for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
-				if (sw.getId().equals(dstDap.getSwitchDPID()) && inPort.equals(dstDap.getPort())) {
-					on_same_if = true;
-				}
-				break;
-			}
-
-			if (on_same_if) {
-				log.info("Both source and destination are on the same switch/port {}/{}. Action = NOP", sw.toString(), inPort);
-				return;
-			}
-
-			SwitchPort[] dstDaps = dstDevice.getAttachmentPoints();
-			SwitchPort dstDap = null;
-
-			/* 
-			 * Search for the true attachment point. The true AP is
-			 * not an endpoint of a link. It is a switch port w/o an
-			 * associated link. Note this does not necessarily hold
-			 * true for devices that 'live' between OpenFlow islands.
-			 * 
-			 * TODO Account for the case where a device is actually
-			 * attached between islands (possibly on a non-OF switch
-			 * in between two OpenFlow switches).
-			 */
-			for (SwitchPort ap : dstDaps) {
-				if (topologyService.isEdge(ap.getSwitchDPID(), ap.getPort())) {
-					dstDap = ap;
-					break;
-				}
-			}	
-
-			/* 
-			 * This should only happen (perhaps) when the controller is
-			 * actively learning a new topology and hasn't discovered
-			 * all links yet, or a switch was in standalone mode and the
-			 * packet in question was captured in flight on the dst point
-			 * of a link.
-			 */
-			if (dstDap == null) {
-				log.warn("Could not locate edge attachment point for device {}. Flooding packet");
-				doFlood(sw, pi, cntx);
-				return; 
-			}
-
-			/* It's possible that we learned packed destination while it was in flight */
-			if (!topologyService.isEdge(source, inPort)) {	
-				log.debug("Packet destination is known, but packet was not received on an edge port (rx on {}/{}). Flooding packet", source, inPort);
-				doFlood(sw, pi, cntx);
-				return; 
-			}				
-
-			Route route = routingEngineService.getRoute(source, 
-					inPort,
-					dstDap.getSwitchDPID(),
-					dstDap.getPort(), U64.of(0)); //cookie = 0, i.e., default route
-
-			Match m = createMatchFromPacket(sw, inPort, cntx);
-			U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
-
-			if (route != null) {
-				log.debug("pushRoute inPort={} route={} " +
-						"destination={}:{}",
-						new Object[] { inPort, route,
-						dstDap.getSwitchDPID(),
-						dstDap.getPort()});
-
-
-				log.debug("Cretaing flow rules on the route, match rule: {}", m);
-				pushRoute(route, m, pi, sw.getId(), cookie, 
-						cntx, requestFlowRemovedNotifn,
-						OFFlowModCommand.ADD);	
-			} else {
-				/* Route traverses no links --> src/dst devices on same switch */
-				log.debug("Could not compute route. Devices should be on same switch src={} and dst={}", srcDevice, dstDevice);
-				Route r = new Route(srcDevice.getAttachmentPoints()[0].getSwitchDPID(), dstDevice.getAttachmentPoints()[0].getSwitchDPID());
-				List<NodePortTuple> path = new ArrayList<NodePortTuple>(2);
-				path.add(new NodePortTuple(srcDevice.getAttachmentPoints()[0].getSwitchDPID(),
-						srcDevice.getAttachmentPoints()[0].getPort()));
-				path.add(new NodePortTuple(dstDevice.getAttachmentPoints()[0].getSwitchDPID(),
-						dstDevice.getAttachmentPoints()[0].getPort()));
-				r.setPath(path);
-				pushRoute(r, m, pi, sw.getId(), cookie,
-						cntx, requestFlowRemovedNotifn,
-						OFFlowModCommand.ADD);
-			}
-		} else {
-			log.debug("Destination unknown. Flooding packet");
-			doFlood(sw, pi, cntx);
-		}
-	}
-
-	/**
-	 * Instead of using the Firewall's routing decision Match, which might be as general
-	 * as "in_port" and inadvertently Match packets erroneously, construct a more
-	 * specific Match based on the deserialized OFPacketIn's payload, which has been 
-	 * placed in the FloodlightContext already by the Controller.
-	 * 
-	 * @param sw, the switch on which the packet was received
-	 * @param inPort, the ingress switch port on which the packet was received
-	 * @param cntx, the current context which contains the deserialized packet
-	 * @return a composed Match object based on the provided information
-	 */
-	protected Match createMatchFromPacket(IOFSwitch sw, OFPort inPort, FloodlightContext cntx) {
-		// The packet in match will only contain the port number.
-		// We need to add in specifics for the hosts we're routing between.
-		Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
-		VlanVid vlan = VlanVid.ofVlan(eth.getVlanID());
-		MacAddress srcMac = eth.getSourceMACAddress();
-		MacAddress dstMac = eth.getDestinationMACAddress();
-
-		Match.Builder mb = sw.getOFFactory().buildMatch();
-		mb.setExact(MatchField.IN_PORT, inPort);
-
-		if (FLOWMOD_DEFAULT_MATCH_MAC) {
-			mb.setExact(MatchField.ETH_SRC, srcMac)
-			.setExact(MatchField.ETH_DST, dstMac);
-		}
-
-		if (FLOWMOD_DEFAULT_MATCH_VLAN) {
-			if (!vlan.equals(VlanVid.ZERO)) {
-				mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid(vlan));
-			}
-		}
-
-		// TODO Detect switch type and match to create hardware-implemented flow
-		if (eth.getEtherType() == EthType.IPv4) { /* shallow check for equality is okay for EthType */
-			IPv4 ip = (IPv4) eth.getPayload();
-			IPv4Address srcIp = ip.getSourceAddress();
-			IPv4Address dstIp = ip.getDestinationAddress();
-
-			if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
-				mb.setExact(MatchField.ETH_TYPE, EthType.IPv4)
-				.setExact(MatchField.IPV4_SRC, srcIp)
-				.setExact(MatchField.IPV4_DST, dstIp);
-			}
-
-			if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) {
-				/*
-				 * Take care of the ethertype if not included earlier,
-				 * since it's a prerequisite for transport ports.
-				 */
-				if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
-					mb.setExact(MatchField.ETH_TYPE, EthType.IPv4);
-				}
-
-				if (ip.getProtocol().equals(IpProtocol.TCP)) {
-					TCP tcp = (TCP) ip.getPayload();
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP)
-					.setExact(MatchField.TCP_SRC, tcp.getSourcePort())
-					.setExact(MatchField.TCP_DST, tcp.getDestinationPort());
-				} else if (ip.getProtocol().equals(IpProtocol.UDP)) {
-					UDP udp = (UDP) ip.getPayload();
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
-					.setExact(MatchField.UDP_SRC, udp.getSourcePort())
-					.setExact(MatchField.UDP_DST, udp.getDestinationPort());
-				}
-			}
-		} else if (eth.getEtherType() == EthType.ARP) { /* shallow check for equality is okay for EthType */
-			mb.setExact(MatchField.ETH_TYPE, EthType.ARP);
-		} else if (eth.getEtherType() == EthType.IPv6) {
-			IPv6 ip = (IPv6) eth.getPayload();
-			IPv6Address srcIp = ip.getSourceAddress();
-			IPv6Address dstIp = ip.getDestinationAddress();
-
-			if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
-				mb.setExact(MatchField.ETH_TYPE, EthType.IPv6)
-				.setExact(MatchField.IPV6_SRC, srcIp)
-				.setExact(MatchField.IPV6_DST, dstIp);
-			}
-
-			if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) {
-				/*
-				 * Take care of the ethertype if not included earlier,
-				 * since it's a prerequisite for transport ports.
-				 */
-				if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
-					mb.setExact(MatchField.ETH_TYPE, EthType.IPv6);
-				}
-
-				if (ip.getNextHeader().equals(IpProtocol.TCP)) {
-					TCP tcp = (TCP) ip.getPayload();
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP)
-					.setExact(MatchField.TCP_SRC, tcp.getSourcePort())
-					.setExact(MatchField.TCP_DST, tcp.getDestinationPort());
-				} else if (ip.getNextHeader().equals(IpProtocol.UDP)) {
-					UDP udp = (UDP) ip.getPayload();
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
-					.setExact(MatchField.UDP_SRC, udp.getSourcePort())
-					.setExact(MatchField.UDP_DST, udp.getDestinationPort());
-				}
-			}
-		}
-		return mb.build();
-	}
-
-	/**
-	 * Creates a OFPacketOut with the OFPacketIn data that is flooded on all ports unless
-	 * the port is blocked, in which case the packet will be dropped.
-	 * @param sw The switch that receives the OFPacketIn
-	 * @param pi The OFPacketIn that came to the switch
-	 * @param cntx The FloodlightContext associated with this OFPacketIn
-	 */
-	protected void doFlood(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
-		OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
-		// Set Action to flood
-		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
-		List<OFAction> actions = new ArrayList<OFAction>();
-		Set<OFPort> broadcastPorts = this.topologyService.getSwitchBroadcastPorts(sw.getId());
-
-		if (broadcastPorts.isEmpty()) {
-			log.warn("No broadcast ports found. Using FLOOD output action");
-			/* Must be a single-switch w/no links */
-			broadcastPorts = Collections.singleton(OFPort.FLOOD);
-		}
-
-		for (OFPort p : broadcastPorts) {
-			if (p.equals(inPort)) continue;
-			actions.add(sw.getOFFactory().actions().output(p, Integer.MAX_VALUE));
-		}
-		pob.setActions(actions);
-		// log.info("actions {}",actions);
-		// set buffer-id, in-port and packet-data based on packet-in
-		pob.setBufferId(OFBufferId.NO_BUFFER);
-		pob.setInPort(inPort);
-		pob.setData(pi.getData());
-
-		try {
-			if (log.isTraceEnabled()) {
-				log.trace("Writing flood PacketOut switch={} packet-in={} packet-out={}",
-						new Object[] {sw, pi, pob.build()});
-			}
-			messageDamper.write(sw, pob.build());
-		} catch (IOException e) {
-			log.error("Failure writing PacketOut switch={} packet-in={} packet-out={}",
-					new Object[] {sw, pi, pob.build()}, e);
-		}
-
-		return;
-	}
-
-	// IFloodlightModule methods
-
-	@Override
-	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
-		// We don't export any services
-		return null;
-	}
-
-	@Override
-	public Map<Class<? extends IFloodlightService>, IFloodlightService>
-	getServiceImpls() {
-		// We don't have any services
-		return null;
-	}
-
-	@Override
-	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
-		Collection<Class<? extends IFloodlightService>> l =
-				new ArrayList<Class<? extends IFloodlightService>>();
-		l.add(IFloodlightProviderService.class);
-		l.add(IDeviceService.class);
-		l.add(IRoutingService.class);
-		l.add(ITopologyService.class);
-		l.add(IDebugCounterService.class);
-		l.add(ILinkDiscoveryService.class);
-		return l;
-	}
-
-	@Override
-	public void init(FloodlightModuleContext context) throws FloodlightModuleException {
-		super.init();
-		this.floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class);
-		this.deviceManagerService = context.getServiceImpl(IDeviceService.class);
-		this.routingEngineService = context.getServiceImpl(IRoutingService.class);
-		this.topologyService = context.getServiceImpl(ITopologyService.class);
-		this.debugCounterService = context.getServiceImpl(IDebugCounterService.class);
-		this.switchService = context.getServiceImpl(IOFSwitchService.class);
-		this.linkService = context.getServiceImpl(ILinkDiscoveryService.class);
-
-		Map<String, String> configParameters = context.getConfigParams(this);
-		String tmp = configParameters.get("hard-timeout");
-		if (tmp != null) {
-			FLOWMOD_DEFAULT_HARD_TIMEOUT = Integer.parseInt(tmp);
-			log.info("Default hard timeout set to {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT);
-		} else {
-			log.info("Default hard timeout not configured. Using {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT);
-		}
-		tmp = configParameters.get("idle-timeout");
-		if (tmp != null) {
-			FLOWMOD_DEFAULT_IDLE_TIMEOUT = Integer.parseInt(tmp);
-			log.info("Default idle timeout set to {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
-		} else {
-			log.info("Default idle timeout not configured. Using {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
-		}
-		tmp = configParameters.get("priority");
-		if (tmp != null) {
-			FLOWMOD_DEFAULT_PRIORITY = 35777;
-			log.info("Default priority set to {}.", FLOWMOD_DEFAULT_PRIORITY);
-		} else {
-			log.info("Default priority not configured. Using {}.", FLOWMOD_DEFAULT_PRIORITY);
-		}
-		tmp = configParameters.get("set-send-flow-rem-flag");
-		if (tmp != null) {
-			FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = Boolean.parseBoolean(tmp);
-			log.info("Default flags will be set to SEND_FLOW_REM.");
-		} else {
-			log.info("Default flags will be empty.");
-		}
-		tmp = configParameters.get("match");
-		if (tmp != null) {
-			tmp = tmp.toLowerCase();
-			if (!tmp.contains("vlan") && !tmp.contains("mac") && !tmp.contains("ip") && !tmp.contains("port")) {
-				/* leave the default configuration -- blank or invalid 'match' value */
-			} else {
-				FLOWMOD_DEFAULT_MATCH_VLAN = tmp.contains("vlan") ? true : false;
-				FLOWMOD_DEFAULT_MATCH_MAC = tmp.contains("mac") ? true : false;
-				FLOWMOD_DEFAULT_MATCH_IP_ADDR = tmp.contains("ip") ? true : false;
-				FLOWMOD_DEFAULT_MATCH_TRANSPORT = tmp.contains("port") ? true : false;
-
-			}
-		}
-		log.info("Default flow matches set to: VLAN=" + FLOWMOD_DEFAULT_MATCH_VLAN
-				+ ", MAC=" + FLOWMOD_DEFAULT_MATCH_MAC
-				+ ", IP=" + FLOWMOD_DEFAULT_MATCH_IP_ADDR
-				+ ", TPPT=" + FLOWMOD_DEFAULT_MATCH_TRANSPORT);
-
-		tmp = configParameters.get("flood-arp");
-		if (tmp != null) {
-			tmp = tmp.toLowerCase();
-			if (!tmp.contains("yes") && !tmp.contains("yep") && !tmp.contains("true") && !tmp.contains("ja") && !tmp.contains("stimmt")) {
-				FLOOD_ALL_ARP_PACKETS = false;
-				log.info("Not flooding ARP packets. ARP flows will be inserted for known destinations");
-			} else {
-				FLOOD_ALL_ARP_PACKETS = true;
-				log.info("Flooding all ARP packets. No ARP flows will be inserted");
-			}
-		}
-
-		tmp = configParameters.get("remove-flows-on-link-or-port-down");
-		if (tmp != null) {
-			REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = Boolean.parseBoolean(tmp);
-		}
-		if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) {
-			log.info("Flows will be removed on link/port down events");
-		} else {
-			log.info("Flows will not be removed on link/port down events");
-		}
-	}
-
-	@Override
-	public void startUp(FloodlightModuleContext context) {
-		super.startUp();
-		switchService.addOFSwitchListener(this);
-
-		/* Register only if we want to remove stale flows */
-		if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) {
-			linkService.addListener(this);
-		}
-	}
-
-	@Override
-	public void switchAdded(DatapathId switchId) {
-	}
-
-	@Override
-	public void switchRemoved(DatapathId switchId) {		
-	}
-
-	@Override
-	public void switchActivated(DatapathId switchId) {
-		IOFSwitch sw = switchService.getSwitch(switchId);
-		if (sw == null) {
-			log.warn("Switch {} was activated but had no switch object in the switch service. Perhaps it quickly disconnected", switchId);
-			return;
-		}
-		if (OFDPAUtils.isOFDPASwitch(sw)) {
-			sw.write(sw.getOFFactory().buildFlowDelete()
-					.setTableId(TableId.ALL)
-					.build()
-					);
-			sw.write(sw.getOFFactory().buildGroupDelete()
-					.setGroup(OFGroup.ANY)
-					.setGroupType(OFGroupType.ALL)
-					.build()
-					);
-			sw.write(sw.getOFFactory().buildGroupDelete()
-					.setGroup(OFGroup.ANY)
-					.setGroupType(OFGroupType.INDIRECT)
-					.build()
-					);
-			sw.write(sw.getOFFactory().buildBarrierRequest().build());
-
-			List<OFPortModeTuple> portModes = new ArrayList<OFPortModeTuple>();
-			for (OFPortDesc p : sw.getPorts()) {
-				portModes.add(OFPortModeTuple.of(p.getPortNo(), OFPortMode.ACCESS));
-			}
-			if (log.isWarnEnabled()) {
-				log.warn("For OF-DPA switch {}, initializing VLAN {} on ports {}", new Object[] { switchId, VlanVid.ZERO, portModes});
-			}
-			OFDPAUtils.addLearningSwitchPrereqs(sw, VlanVid.ZERO, portModes);
-		}
-	}
-
-	@Override
-	public void switchPortChanged(DatapathId switchId, OFPortDesc port, PortChangeType type) {	
-		/* Port down events handled via linkDiscoveryUpdate(), which passes thru all events */
-	}
-
-	@Override
-	public void switchChanged(DatapathId switchId) {
-	}
-
-	@Override
-	public void switchDeactivated(DatapathId switchId) {
-	}
-
-	@Override
-	public void linkDiscoveryUpdate(List<LDUpdate> updateList) {
-		for (LDUpdate u : updateList) {
-			/* Remove flows on either side if link/port went down */
-			if (u.getOperation() == UpdateOperation.LINK_REMOVED ||
-					u.getOperation() == UpdateOperation.PORT_DOWN ||
-					u.getOperation() == UpdateOperation.TUNNEL_PORT_REMOVED) {
-				Set<OFMessage> msgs = new HashSet<OFMessage>();
-
-				if (u.getSrc() != null && !u.getSrc().equals(DatapathId.NONE)) {
-					IOFSwitch srcSw = switchService.getSwitch(u.getSrc());
-					/* src side of link */
-					if (srcSw != null) {
-						/* flows matching on src port */
-						msgs.add(srcSw.getOFFactory().buildFlowDelete()
-								.setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
-								.setMatch(srcSw.getOFFactory().buildMatch()
-										.setExact(MatchField.IN_PORT, u.getSrcPort())
-										.build())
-										.build());
-						/* flows outputting to src port */
-						msgs.add(srcSw.getOFFactory().buildFlowDelete()
-								.setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
-								.setOutPort(u.getSrcPort())
-								.build());
-						srcSw.write(msgs);
-						log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getSrc(), u.getSrcPort() });
-					}
-				}
-
-				/* must be a link, not just a port down, if we have a dst switch */
-				if (u.getDst() != null && !u.getDst().equals(DatapathId.NONE)) {
-					/* dst side of link */
-					IOFSwitch dstSw = switchService.getSwitch(u.getDst());
-					if (dstSw != null) {
-						/* flows matching on dst port */
-						msgs.clear();
-						msgs.add(dstSw.getOFFactory().buildFlowDelete()
-								.setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
-								.setMatch(dstSw.getOFFactory().buildMatch()
-										.setExact(MatchField.IN_PORT, u.getDstPort())
-										.build())
-										.build());
-						/* flows outputting to dst port */
-						msgs.add(dstSw.getOFFactory().buildFlowDelete()
-								.setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
-								.setOutPort(u.getDstPort())
-								.build());
-						dstSw.write(msgs);
-						log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getDst(), u.getDstPort() });
-					}
-				}
-			}
-		}
-	}
+    protected static Logger log = LoggerFactory.getLogger(Forwarding.class);
+
+    @Override
+    public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
+        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+        // We found a routing decision (i.e. Firewall is enabled... it's the only thing that makes RoutingDecisions)
+        if (decision != null) {
+            if (log.isTraceEnabled()) {
+                log.trace("Forwarding decision={} was made for PacketIn={}", decision.getRoutingAction().toString(), pi);
+            }
+
+            switch(decision.getRoutingAction()) {
+            case NONE:
+                // don't do anything
+                return Command.CONTINUE;
+            case FORWARD_OR_FLOOD:
+            case FORWARD:
+                doForwardFlow(sw, pi, cntx, false);
+                return Command.CONTINUE;
+            case MULTICAST:
+                // treat as broadcast
+                doFlood(sw, pi, cntx);
+                return Command.CONTINUE;
+            case DROP:
+                doDropFlow(sw, pi, decision, cntx);
+                return Command.CONTINUE;
+            default:
+                log.error("Unexpected decision made for this packet-in={}", pi, decision.getRoutingAction());
+                return Command.CONTINUE;
+            }
+        } else { // No routing decision was found. Forward to destination or flood if bcast or mcast.
+            if (log.isTraceEnabled()) {
+                log.trace("No decision was made for PacketIn={}, forwarding", pi);
+            }
+
+            if (eth.isBroadcast() || eth.isMulticast()) {
+                doFlood(sw, pi, cntx);
+            } else {
+                doForwardFlow(sw, pi, cntx, false);
+            }
+        }
+
+        return Command.CONTINUE;
+    }
+
+    protected void doDropFlow(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
+        OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
+        Match m = createMatchFromPacket(sw, inPort, cntx);
+        OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd(); // this will be a drop-flow; a flow that will not output to any ports
+        List<OFAction> actions = new ArrayList<OFAction>(); // set no action to drop
+        U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
+        log.info("Dropping");
+        fmb.setCookie(cookie)
+        .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+        .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+        .setBufferId(OFBufferId.NO_BUFFER) 
+        .setMatch(m)
+        .setPriority(FLOWMOD_DEFAULT_PRIORITY);
+
+        FlowModUtils.setActions(fmb, actions, sw);
+
+        /* Configure for particular switch pipeline */
+        fmb.setTableId(FLOWMOD_DEFAULT_TABLE_ID);
+
+        if (log.isDebugEnabled()) {
+            log.debug("write drop flow-mod sw={} match={} flow-mod={}",
+                    new Object[] { sw, m, fmb.build() });
+        }
+        boolean dampened = messageDamper.write(sw, fmb.build());
+        log.debug("OFMessage dampened: {}", dampened);
+    }
+
+    protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx, boolean requestFlowRemovedNotifn) {
+        OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
+        IDevice dstDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE);
+        DatapathId source = sw.getId();
+
+        if (dstDevice != null) {
+            IDevice srcDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE);
+
+            if (srcDevice == null) {
+                log.error("No device entry found for source device. Is the device manager running? If so, report bug.");
+                return;
+            }
+
+            if (FLOOD_ALL_ARP_PACKETS && 
+                    IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD).getEtherType() 
+                    == EthType.ARP) {
+                log.debug("ARP flows disabled in Forwarding. Flooding ARP packet");
+                doFlood(sw, pi, cntx);
+                return;
+            }
+
+            /* Validate that the source and destination are not on the same switch port */
+            boolean on_same_if = false;
+            for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
+                if (sw.getId().equals(dstDap.getNodeId()) && inPort.equals(dstDap.getPortId())) {
+                    on_same_if = true;
+                }
+                break;
+            }
+
+            if (on_same_if) {
+                log.info("Both source and destination are on the same switch/port {}/{}. Action = NOP", sw.toString(), inPort);
+                return;
+            }
+
+            SwitchPort[] dstDaps = dstDevice.getAttachmentPoints();
+            SwitchPort dstDap = null;
+
+            /* 
+             * Search for the true attachment point. The true AP is
+             * not an endpoint of a link. It is a switch port w/o an
+             * associated link. Note this does not necessarily hold
+             * true for devices that 'live' between OpenFlow islands.
+             * 
+             * TODO Account for the case where a device is actually
+             * attached between islands (possibly on a non-OF switch
+             * in between two OpenFlow switches).
+             */
+            for (SwitchPort ap : dstDaps) {
+                if (topologyService.isEdge(ap.getNodeId(), ap.getPortId())) {
+                    dstDap = ap;
+                    break;
+                }
+            }	
+
+            /* 
+             * This should only happen (perhaps) when the controller is
+             * actively learning a new topology and hasn't discovered
+             * all links yet, or a switch was in standalone mode and the
+             * packet in question was captured in flight on the dst point
+             * of a link.
+             */
+            if (dstDap == null) {
+                log.warn("Could not locate edge attachment point for device {}. Flooding packet");
+                doFlood(sw, pi, cntx);
+                return; 
+            }
+
+            /* It's possible that we learned packed destination while it was in flight */
+            if (!topologyService.isEdge(source, inPort)) {	
+                log.debug("Packet destination is known, but packet was not received on an edge port (rx on {}/{}). Flooding packet", source, inPort);
+                doFlood(sw, pi, cntx);
+                return; 
+            }				
+
+            Route route = routingEngineService.getRoute(source, 
+                    inPort,
+                    dstDap.getNodeId(),
+                    dstDap.getPortId(), U64.of(0)); //cookie = 0, i.e., default route
+
+            Match m = createMatchFromPacket(sw, inPort, cntx);
+            U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
+
+            if (route != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("pushRoute inPort={} route={} " +
+                            "destination={}:{}",
+                            new Object[] { inPort, route,
+                                    dstDap.getNodeId(),
+                                    dstDap.getPortId()});
+                }
+
+
+                log.debug("Cretaing flow rules on the route, match rule: {}", m);
+                pushRoute(route, m, pi, sw.getId(), cookie, 
+                        cntx, requestFlowRemovedNotifn,
+                        OFFlowModCommand.ADD);	
+            } else {
+                /* Route traverses no links --> src/dst devices on same switch */
+                log.debug("Could not compute route. Devices should be on same switch src={} and dst={}", srcDevice, dstDevice);
+                Route r = new Route(srcDevice.getAttachmentPoints()[0].getNodeId(), dstDevice.getAttachmentPoints()[0].getNodeId());
+                List<NodePortTuple> path = new ArrayList<NodePortTuple>(2);
+                path.add(new NodePortTuple(srcDevice.getAttachmentPoints()[0].getNodeId(),
+                        srcDevice.getAttachmentPoints()[0].getPortId()));
+                path.add(new NodePortTuple(dstDevice.getAttachmentPoints()[0].getNodeId(),
+                        dstDevice.getAttachmentPoints()[0].getPortId()));
+                r.setPath(path);
+                pushRoute(r, m, pi, sw.getId(), cookie,
+                        cntx, requestFlowRemovedNotifn,
+                        OFFlowModCommand.ADD);
+            }
+        } else {
+            log.debug("Destination unknown. Flooding packet");
+            doFlood(sw, pi, cntx);
+        }
+    }
+
+    /**
+     * Instead of using the Firewall's routing decision Match, which might be as general
+     * as "in_port" and inadvertently Match packets erroneously, construct a more
+     * specific Match based on the deserialized OFPacketIn's payload, which has been 
+     * placed in the FloodlightContext already by the Controller.
+     * 
+     * @param sw, the switch on which the packet was received
+     * @param inPort, the ingress switch port on which the packet was received
+     * @param cntx, the current context which contains the deserialized packet
+     * @return a composed Match object based on the provided information
+     */
+    protected Match createMatchFromPacket(IOFSwitch sw, OFPort inPort, FloodlightContext cntx) {
+        // The packet in match will only contain the port number.
+        // We need to add in specifics for the hosts we're routing between.
+        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+        VlanVid vlan = VlanVid.ofVlan(eth.getVlanID());
+        MacAddress srcMac = eth.getSourceMACAddress();
+        MacAddress dstMac = eth.getDestinationMACAddress();
+
+        Match.Builder mb = sw.getOFFactory().buildMatch();
+        mb.setExact(MatchField.IN_PORT, inPort);
+
+        if (FLOWMOD_DEFAULT_MATCH_MAC) {
+            mb.setExact(MatchField.ETH_SRC, srcMac)
+            .setExact(MatchField.ETH_DST, dstMac);
+        }
+
+        if (FLOWMOD_DEFAULT_MATCH_VLAN) {
+            if (!vlan.equals(VlanVid.ZERO)) {
+                mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid(vlan));
+            }
+        }
+
+        // TODO Detect switch type and match to create hardware-implemented flow
+        if (eth.getEtherType() == EthType.IPv4) { /* shallow check for equality is okay for EthType */
+            IPv4 ip = (IPv4) eth.getPayload();
+            IPv4Address srcIp = ip.getSourceAddress();
+            IPv4Address dstIp = ip.getDestinationAddress();
+
+            if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
+                mb.setExact(MatchField.ETH_TYPE, EthType.IPv4)
+                .setExact(MatchField.IPV4_SRC, srcIp)
+                .setExact(MatchField.IPV4_DST, dstIp);
+            }
+
+            if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) {
+                /*
+                 * Take care of the ethertype if not included earlier,
+                 * since it's a prerequisite for transport ports.
+                 */
+                if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
+                    mb.setExact(MatchField.ETH_TYPE, EthType.IPv4);
+                }
+
+                if (ip.getProtocol().equals(IpProtocol.TCP)) {
+                    TCP tcp = (TCP) ip.getPayload();
+                    mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP)
+                    .setExact(MatchField.TCP_SRC, tcp.getSourcePort())
+                    .setExact(MatchField.TCP_DST, tcp.getDestinationPort());
+                } else if (ip.getProtocol().equals(IpProtocol.UDP)) {
+                    UDP udp = (UDP) ip.getPayload();
+                    mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
+                    .setExact(MatchField.UDP_SRC, udp.getSourcePort())
+                    .setExact(MatchField.UDP_DST, udp.getDestinationPort());
+                }
+            }
+        } else if (eth.getEtherType() == EthType.ARP) { /* shallow check for equality is okay for EthType */
+            mb.setExact(MatchField.ETH_TYPE, EthType.ARP);
+        } else if (eth.getEtherType() == EthType.IPv6) {
+            IPv6 ip = (IPv6) eth.getPayload();
+            IPv6Address srcIp = ip.getSourceAddress();
+            IPv6Address dstIp = ip.getDestinationAddress();
+
+            if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
+                mb.setExact(MatchField.ETH_TYPE, EthType.IPv6)
+                .setExact(MatchField.IPV6_SRC, srcIp)
+                .setExact(MatchField.IPV6_DST, dstIp);
+            }
+
+            if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) {
+                /*
+                 * Take care of the ethertype if not included earlier,
+                 * since it's a prerequisite for transport ports.
+                 */
+                if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
+                    mb.setExact(MatchField.ETH_TYPE, EthType.IPv6);
+                }
+
+                if (ip.getNextHeader().equals(IpProtocol.TCP)) {
+                    TCP tcp = (TCP) ip.getPayload();
+                    mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP)
+                    .setExact(MatchField.TCP_SRC, tcp.getSourcePort())
+                    .setExact(MatchField.TCP_DST, tcp.getDestinationPort());
+                } else if (ip.getNextHeader().equals(IpProtocol.UDP)) {
+                    UDP udp = (UDP) ip.getPayload();
+                    mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
+                    .setExact(MatchField.UDP_SRC, udp.getSourcePort())
+                    .setExact(MatchField.UDP_DST, udp.getDestinationPort());
+                }
+            }
+        }
+        return mb.build();
+    }
+
+    /**
+     * Creates a OFPacketOut with the OFPacketIn data that is flooded on all ports unless
+     * the port is blocked, in which case the packet will be dropped.
+     * @param sw The switch that receives the OFPacketIn
+     * @param pi The OFPacketIn that came to the switch
+     * @param cntx The FloodlightContext associated with this OFPacketIn
+     */
+    protected void doFlood(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
+        OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
+        // Set Action to flood
+        OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
+        List<OFAction> actions = new ArrayList<OFAction>();
+        Set<OFPort> broadcastPorts = this.topologyService.getSwitchBroadcastPorts(sw.getId());
+
+        if (broadcastPorts.isEmpty()) {
+            log.warn("No broadcast ports found. Using FLOOD output action");
+            /* Must be a single-switch w/no links */
+            broadcastPorts = Collections.singleton(OFPort.FLOOD);
+        }
+
+        for (OFPort p : broadcastPorts) {
+            if (p.equals(inPort)) continue;
+            actions.add(sw.getOFFactory().actions().output(p, Integer.MAX_VALUE));
+        }
+        pob.setActions(actions);
+        // log.info("actions {}",actions);
+        // set buffer-id, in-port and packet-data based on packet-in
+        pob.setBufferId(OFBufferId.NO_BUFFER);
+        OFMessageUtils.setInPort(pob, inPort);
+        pob.setData(pi.getData());
+
+        if (log.isTraceEnabled()) {
+            log.trace("Writing flood PacketOut switch={} packet-in={} packet-out={}",
+                    new Object[] {sw, pi, pob.build()});
+        }
+        messageDamper.write(sw, pob.build());
+
+        return;
+    }
+
+    // IFloodlightModule methods
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+        // We don't export any services
+        return null;
+    }
+
+    @Override
+    public Map<Class<? extends IFloodlightService>, IFloodlightService>
+    getServiceImpls() {
+        // We don't have any services
+        return null;
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+        Collection<Class<? extends IFloodlightService>> l =
+                new ArrayList<Class<? extends IFloodlightService>>();
+        l.add(IFloodlightProviderService.class);
+        l.add(IDeviceService.class);
+        l.add(IRoutingService.class);
+        l.add(ITopologyService.class);
+        l.add(IDebugCounterService.class);
+        l.add(ILinkDiscoveryService.class);
+        return l;
+    }
+
+    @Override
+    public void init(FloodlightModuleContext context) throws FloodlightModuleException {
+        super.init();
+        this.floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class);
+        this.deviceManagerService = context.getServiceImpl(IDeviceService.class);
+        this.routingEngineService = context.getServiceImpl(IRoutingService.class);
+        this.topologyService = context.getServiceImpl(ITopologyService.class);
+        this.debugCounterService = context.getServiceImpl(IDebugCounterService.class);
+        this.switchService = context.getServiceImpl(IOFSwitchService.class);
+        this.linkService = context.getServiceImpl(ILinkDiscoveryService.class);
+
+        Map<String, String> configParameters = context.getConfigParams(this);
+        String tmp = configParameters.get("hard-timeout");
+        if (tmp != null) {
+            FLOWMOD_DEFAULT_HARD_TIMEOUT = ParseUtils.parseHexOrDecInt(tmp);
+            log.info("Default hard timeout set to {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT);
+        } else {
+            log.info("Default hard timeout not configured. Using {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT);
+        }
+        tmp = configParameters.get("idle-timeout");
+        if (tmp != null) {
+            FLOWMOD_DEFAULT_IDLE_TIMEOUT = ParseUtils.parseHexOrDecInt(tmp);
+            log.info("Default idle timeout set to {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
+        } else {
+            log.info("Default idle timeout not configured. Using {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
+        }
+        tmp = configParameters.get("table-id");
+        if (tmp != null) {
+            FLOWMOD_DEFAULT_TABLE_ID = TableId.of(ParseUtils.parseHexOrDecInt(tmp));
+            log.info("Default idle timeout set to {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
+        } else {
+            log.info("Default idle timeout not configured. Using {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
+        }
+        tmp = configParameters.get("priority");
+        if (tmp != null) {
+            FLOWMOD_DEFAULT_PRIORITY = ParseUtils.parseHexOrDecInt(tmp);
+            log.info("Default priority set to {}.", FLOWMOD_DEFAULT_PRIORITY);
+        } else {
+            log.info("Default priority not configured. Using {}.", FLOWMOD_DEFAULT_PRIORITY);
+        }
+        tmp = configParameters.get("set-send-flow-rem-flag");
+        if (tmp != null) {
+            FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = Boolean.parseBoolean(tmp);
+            log.info("Default flags will be set to SEND_FLOW_REM {}.", FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG);
+        } else {
+            log.info("Default flags will be empty.");
+        }
+        tmp = configParameters.get("match");
+        if (tmp != null) {
+            tmp = tmp.toLowerCase();
+            if (!tmp.contains("vlan") && !tmp.contains("mac") && !tmp.contains("ip") && !tmp.contains("port")) {
+                /* leave the default configuration -- blank or invalid 'match' value */
+            } else {
+                FLOWMOD_DEFAULT_MATCH_VLAN = tmp.contains("vlan") ? true : false;
+                FLOWMOD_DEFAULT_MATCH_MAC = tmp.contains("mac") ? true : false;
+                FLOWMOD_DEFAULT_MATCH_IP_ADDR = tmp.contains("ip") ? true : false;
+                FLOWMOD_DEFAULT_MATCH_TRANSPORT = tmp.contains("port") ? true : false;
+
+            }
+        }
+        log.info("Default flow matches set to: VLAN=" + FLOWMOD_DEFAULT_MATCH_VLAN
+                + ", MAC=" + FLOWMOD_DEFAULT_MATCH_MAC
+                + ", IP=" + FLOWMOD_DEFAULT_MATCH_IP_ADDR
+                + ", TPPT=" + FLOWMOD_DEFAULT_MATCH_TRANSPORT);
+
+        tmp = configParameters.get("flood-arp");
+        if (tmp != null) {
+            tmp = tmp.toLowerCase();
+            if (!tmp.contains("yes") && !tmp.contains("yep") && !tmp.contains("true") && !tmp.contains("ja") && !tmp.contains("stimmt")) {
+                FLOOD_ALL_ARP_PACKETS = false;
+                log.info("Not flooding ARP packets. ARP flows will be inserted for known destinations");
+            } else {
+                FLOOD_ALL_ARP_PACKETS = true;
+                log.info("Flooding all ARP packets. No ARP flows will be inserted");
+            }
+        }
+
+        tmp = configParameters.get("remove-flows-on-link-or-port-down");
+        if (tmp != null) {
+            REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = Boolean.parseBoolean(tmp);
+        }
+        if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) {
+            log.info("Flows will be removed on link/port down events");
+        } else {
+            log.info("Flows will not be removed on link/port down events");
+        }
+    }
+
+    @Override
+    public void startUp(FloodlightModuleContext context) {
+        super.startUp();
+        switchService.addOFSwitchListener(this);
+
+        /* Register only if we want to remove stale flows */
+        if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) {
+            linkService.addListener(this);
+        }
+    }
+
+    @Override
+    public void switchAdded(DatapathId switchId) {
+    }
+
+    @Override
+    public void switchRemoved(DatapathId switchId) {		
+    }
+
+    @Override
+    public void switchActivated(DatapathId switchId) {
+        IOFSwitch sw = switchService.getSwitch(switchId);
+        if (sw == null) {
+            log.warn("Switch {} was activated but had no switch object in the switch service. Perhaps it quickly disconnected", switchId);
+            return;
+        }
+        if (OFDPAUtils.isOFDPASwitch(sw)) {
+            messageDamper.write(sw, sw.getOFFactory().buildFlowDelete()
+                    .setTableId(TableId.ALL)
+                    .build()
+                    );
+            messageDamper.write(sw, sw.getOFFactory().buildGroupDelete()
+                    .setGroup(OFGroup.ANY)
+                    .setGroupType(OFGroupType.ALL)
+                    .build()
+                    );
+            messageDamper.write(sw, sw.getOFFactory().buildGroupDelete()
+                    .setGroup(OFGroup.ANY)
+                    .setGroupType(OFGroupType.INDIRECT)
+                    .build()
+                    );
+            messageDamper.write(sw, sw.getOFFactory().buildBarrierRequest().build());
+
+            List<OFPortModeTuple> portModes = new ArrayList<OFPortModeTuple>();
+            for (OFPortDesc p : sw.getPorts()) {
+                portModes.add(OFPortModeTuple.of(p.getPortNo(), OFPortMode.ACCESS));
+            }
+            if (log.isWarnEnabled()) {
+                log.warn("For OF-DPA switch {}, initializing VLAN {} on ports {}", new Object[] { switchId, VlanVid.ZERO, portModes});
+            }
+            OFDPAUtils.addLearningSwitchPrereqs(sw, VlanVid.ZERO, portModes);
+        }
+    }
+
+    @Override
+    public void switchPortChanged(DatapathId switchId, OFPortDesc port, PortChangeType type) {	
+        /* Port down events handled via linkDiscoveryUpdate(), which passes thru all events */
+    }
+
+    @Override
+    public void switchChanged(DatapathId switchId) {
+    }
+
+    @Override
+    public void switchDeactivated(DatapathId switchId) {
+    }
+
+    @Override
+    public void linkDiscoveryUpdate(List<LDUpdate> updateList) {
+        for (LDUpdate u : updateList) {
+            /* Remove flows on either side if link/port went down */
+            if (u.getOperation() == UpdateOperation.LINK_REMOVED ||
+                    u.getOperation() == UpdateOperation.PORT_DOWN ||
+                    u.getOperation() == UpdateOperation.TUNNEL_PORT_REMOVED) {
+                Set<OFMessage> msgs = new HashSet<OFMessage>();
+
+                if (u.getSrc() != null && !u.getSrc().equals(DatapathId.NONE)) {
+                    IOFSwitch srcSw = switchService.getSwitch(u.getSrc());
+                    /* src side of link */
+                    if (srcSw != null) {
+                        /* flows matching on src port */
+                        msgs.add(srcSw.getOFFactory().buildFlowDelete()
+                                .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
+                                .setMatch(srcSw.getOFFactory().buildMatch()
+                                        .setExact(MatchField.IN_PORT, u.getSrcPort())
+                                        .build())
+                                .build());
+                        /* flows outputting to src port */
+                        msgs.add(srcSw.getOFFactory().buildFlowDelete()
+                                .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
+                                .setOutPort(u.getSrcPort())
+                                .build());
+                        messageDamper.write(srcSw, msgs);
+                        log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getSrc(), u.getSrcPort() });
+                    }
+                }
+
+                /* must be a link, not just a port down, if we have a dst switch */
+                if (u.getDst() != null && !u.getDst().equals(DatapathId.NONE)) {
+                    /* dst side of link */
+                    IOFSwitch dstSw = switchService.getSwitch(u.getDst());
+                    if (dstSw != null) {
+                        /* flows matching on dst port */
+                        msgs.clear();
+                        msgs.add(dstSw.getOFFactory().buildFlowDelete()
+                                .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
+                                .setMatch(dstSw.getOFFactory().buildMatch()
+                                        .setExact(MatchField.IN_PORT, u.getDstPort())
+                                        .build())
+                                .build());
+                        /* flows outputting to dst port */
+                        msgs.add(dstSw.getOFFactory().buildFlowDelete()
+                                .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
+                                .setOutPort(u.getDstPort())
+                                .build());
+                        messageDamper.write(dstSw, msgs);
+                        log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getDst(), u.getDstPort() });
+                    }
+                }
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index bd69e302f987c77fde08b2b0ed852de020654af4..d9871f991951718518643f6fe29b3cdadd591368 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -62,11 +62,6 @@ import net.floodlightcontroller.core.types.NodePortTuple;
 import net.floodlightcontroller.core.util.SingletonTask;
 import net.floodlightcontroller.debugcounter.IDebugCounter;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType;
-import net.floodlightcontroller.debugevent.IEventCategory;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventType;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType;
@@ -75,8 +70,6 @@ import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.UpdateOperation;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService;
 import net.floodlightcontroller.linkdiscovery.web.LinkDiscoveryWebRoutable;
-import net.floodlightcontroller.notification.INotificationManager;
-import net.floodlightcontroller.notification.NotificationManagerFactory;
 import net.floodlightcontroller.packet.BSN;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.LLDP;
@@ -89,6 +82,7 @@ import net.floodlightcontroller.storage.IStorageSourceService;
 import net.floodlightcontroller.storage.OperatorPredicate;
 import net.floodlightcontroller.storage.StorageException;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
+import net.floodlightcontroller.util.OFMessageUtils;
 
 import org.projectfloodlight.openflow.protocol.OFControllerRole;
 import org.projectfloodlight.openflow.protocol.OFMessage;
@@ -103,7 +97,6 @@ import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.U64;
-import org.projectfloodlight.openflow.util.HexString;
 import org.projectfloodlight.openflow.protocol.OFType;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
@@ -131,8 +124,6 @@ public class LinkDiscoveryManager implements IOFMessageListener,
 IOFSwitchListener, IStorageSourceListener, ILinkDiscoveryService,
 IFloodlightModule, IInfoProvider {
 	protected static final Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class);
-	protected static final INotificationManager notifier =
-			NotificationManagerFactory.getNotificationManager(LinkDiscoveryManager.class);
 
 	public static final String MODULE_NAME = "linkdiscovery";
 
@@ -151,16 +142,12 @@ IFloodlightModule, IInfoProvider {
 	private static final String LINK_TYPE = "link_type";
 	private static final String SWITCH_CONFIG_TABLE_NAME = "controller_switchconfig";
 
-	// Event updaters for debug events
-	protected IEventCategory<DirectLinkEvent> eventCategory;
-
 	protected IFloodlightProviderService floodlightProviderService;
 	protected IOFSwitchService switchService;
 	protected IStorageSourceService storageSourceService;
 	protected IThreadPoolService threadPoolService;
 	protected IRestApiService restApiService;
 	protected IDebugCounterService debugCounterService;
-	protected IDebugEventService debugEventService;
 	protected IShutdownService shutdownService;
 
 	// Role
@@ -168,7 +155,7 @@ IFloodlightModule, IInfoProvider {
 
 	// LLDP and BDDP fields
 	private static final byte[] LLDP_STANDARD_DST_MAC_STRING =
-			HexString.fromHexString("01:80:c2:00:00:0e");
+			MacAddress.of("01:80:c2:00:00:0e").getBytes();
 	private static final long LINK_LOCAL_MASK = 0xfffffffffff0L;
 	private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
 	protected static int EVENT_HISTORY_SIZE = 1024; // in seconds
@@ -293,7 +280,7 @@ IFloodlightModule, IInfoProvider {
 	//*********************
 
 	@Override
-	public OFPacketOut generateLLDPMessage(IOFSwitch iofSwitch, OFPort port,
+	public OFPacketOut generateLLDPMessage(IOFSwitch iofSwitch, OFPort port, 
 			boolean isStandard, boolean isReverse) {
 
 		OFPortDesc ofpPort = iofSwitch.getPort(port);
@@ -416,13 +403,13 @@ IFloodlightModule, IInfoProvider {
 
 		// serialize and wrap in a packet out
 		byte[] data = ethernet.serialize();
-		OFPacketOut.Builder pob = iofSwitch.getOFFactory().buildPacketOut();
-		pob.setBufferId(OFBufferId.NO_BUFFER);
-		pob.setInPort(OFPort.CONTROLLER);
-
-		// set data and data length
-		pob.setData(data);
+		OFPacketOut.Builder pob = iofSwitch.getOFFactory().buildPacketOut()
+		.setBufferId(OFBufferId.NO_BUFFER)
+		.setActions(getDiscoveryActions(iofSwitch, port))
+		.setData(data);
+		OFMessageUtils.setInPort(pob, OFPort.CONTROLLER);
 
+		log.debug("{}", pob.build());
 		return pob.build();
 	}
 
@@ -1178,7 +1165,7 @@ IFloodlightModule, IInfoProvider {
 	 * @param port
 	 * @return
 	 */
-	protected List<OFAction> getDiscoveryActions (IOFSwitch sw, OFPort port){
+	protected List<OFAction> getDiscoveryActions(IOFSwitch sw, OFPort port) {
 		// set actions
 		List<OFAction> actions = new ArrayList<OFAction>();
 		actions.add(sw.getOFFactory().actions().buildOutput().setPort(port).build());
@@ -1202,26 +1189,16 @@ IFloodlightModule, IInfoProvider {
 			boolean isStandard, boolean isReverse) {
 
 		// Takes care of all checks including null pointer checks.
-		if (!isOutgoingDiscoveryAllowed(sw, port, isStandard, isReverse))
+		if (!isOutgoingDiscoveryAllowed(sw, port, isStandard, isReverse)) {
 			return false;
+		}
 
 		IOFSwitch iofSwitch = switchService.getSwitch(sw);
-		if (iofSwitch == null)             //fix dereference violations in case race conditions
+		if (iofSwitch == null) { // fix dereference violations in case race conditions
 			return false;
-		OFPortDesc ofpPort = iofSwitch.getPort(port);
-
-		OFPacketOut po = generateLLDPMessage(iofSwitch, port, isStandard, isReverse);
-		OFPacketOut.Builder pob = po.createBuilder();
-
-		// Add actions
-		List<OFAction> actions = getDiscoveryActions(iofSwitch, ofpPort.getPortNo());
-		pob.setActions(actions);
-
-		// no need to set length anymore
+		}
 
-		// send
-		// no more try-catch. switch will silently fail
-		return iofSwitch.write(pob.build());
+		return iofSwitch.write(generateLLDPMessage(iofSwitch, port, isStandard, isReverse));
 	}
 
 	/**
@@ -1435,10 +1412,7 @@ IFloodlightModule, IInfoProvider {
 				LinkType linkType = getLinkType(lt, newInfo);
 				if (linkType == ILinkDiscovery.LinkType.DIRECT_LINK) {
 					log.debug("Inter-switch link detected: {}", lt);
-					eventCategory.newEventNoFlush(new DirectLinkEvent(lt.getSrc(),
-							lt.getSrcPort(), lt.getDst(), lt.getDstPort(), "direct-link-added::rcvd LLDP"));
 				}
-				notifier.postNotification("Link added: " + lt.toString());
 			} else {
 				linkChanged = updateLink(lt, existingInfo, newInfo);
 				if (linkChanged) {
@@ -1446,11 +1420,7 @@ IFloodlightModule, IInfoProvider {
 					LinkType linkType = getLinkType(lt, newInfo);
 					if (linkType == ILinkDiscovery.LinkType.DIRECT_LINK) {
 						log.debug("Inter-switch link updated: {}", lt);
-						eventCategory.newEventNoFlush(new DirectLinkEvent(lt.getSrc(),
-								lt.getSrcPort(), lt.getDst(), lt.getDstPort(),
-								"link-port-state-updated::rcvd LLDP"));
 					}
-					notifier.postNotification("Link updated: " + lt.toString());
 				}
 			}
 
@@ -1558,11 +1528,6 @@ IFloodlightModule, IInfoProvider {
 						linkType,
 						UpdateOperation.LINK_REMOVED));
 
-				// FIXME: link type shows up as invalid now -- thus not checking if
-				// link type is a direct link
-				eventCategory.newEventWithFlush(new DirectLinkEvent(lt.getSrc(),
-						lt.getSrcPort(), lt.getDst(), lt.getDstPort(),
-						"link-deleted::" + reason));
 				// remove link from storage.
 				removeLinkFromStorage(lt);
 
@@ -1571,8 +1536,6 @@ IFloodlightModule, IInfoProvider {
 
 				if (linkType == ILinkDiscovery.LinkType.DIRECT_LINK) {
 					log.info("Inter-switch link removed: {}", lt);
-					notifier.postNotification("Inter-switch link removed: " +
-							lt.toString());
 				} else if (log.isTraceEnabled()) {
 					log.trace("Deleted link {}", lt);
 				}
@@ -2001,7 +1964,6 @@ IFloodlightModule, IInfoProvider {
 		threadPoolService = context.getServiceImpl(IThreadPoolService.class);
 		restApiService = context.getServiceImpl(IRestApiService.class);
 		debugCounterService = context.getServiceImpl(IDebugCounterService.class);
-		debugEventService = context.getServiceImpl(IDebugEventService.class);
 		shutdownService = context.getServiceImpl(IShutdownService.class);
 
 		// read our config options
@@ -2056,7 +2018,6 @@ IFloodlightModule, IInfoProvider {
 				new ConcurrentHashMap<MACRange,Boolean>());
 		this.haListener = new HAListenerDelegate();
 		registerLinkDiscoveryDebugCounters();
-		registerLinkDiscoveryDebugEvents();
 	}
 
 	@Override
@@ -2178,46 +2139,6 @@ IFloodlightModule, IInfoProvider {
 				"All packets arriving on quarantined ports dropped by this module", IDebugCounterService.MetaData.WARN);
 	}
 
-	private void registerLinkDiscoveryDebugEvents() throws FloodlightModuleException {
-		if (debugEventService == null) {
-			log.error("Debug Event Service not found.");
-		}
-
-		eventCategory = debugEventService.buildEvent(DirectLinkEvent.class)
-				.setModuleName(PACKAGE)
-				.setEventName("linkevent")
-				.setEventDescription("Direct OpenFlow links discovered or timed-out")
-				.setEventType(EventType.ALWAYS_LOG)
-				.setBufferCapacity(100)
-				.register();
-	}
-
-	public class DirectLinkEvent {
-		@EventColumn(name = "srcSw", description = EventFieldType.DPID)
-		DatapathId srcDpid;
-
-		@EventColumn(name = "srcPort", description = EventFieldType.PRIMITIVE)
-		OFPort srcPort;
-
-		@EventColumn(name = "dstSw", description = EventFieldType.DPID)
-		DatapathId dstDpid;
-
-		@EventColumn(name = "dstPort", description = EventFieldType.PRIMITIVE)
-		OFPort dstPort;
-
-		@EventColumn(name = "reason", description = EventFieldType.STRING)
-		String reason;
-
-		public DirectLinkEvent(DatapathId srcDpid, OFPort srcPort, DatapathId dstDpid,
-				OFPort dstPort, String reason) {
-			this.srcDpid = srcDpid;
-			this.srcPort = srcPort;
-			this.dstDpid = dstDpid;
-			this.dstPort = dstPort;
-			this.reason = reason;
-		}
-	}
-
 	//*********************
 	//  IInfoProvider
 	//*********************
diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
index fed30ede65fb0f120c6c4d99fb7c2090250fd8ff..a4a0435f4af08955aa09a0cecff177d856448655 100644
--- a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
+++ b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
@@ -73,9 +73,10 @@ import net.floodlightcontroller.packet.UDP;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.routing.IRoutingService;
 import net.floodlightcontroller.routing.Route;
-import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
+import net.floodlightcontroller.staticentry.IStaticEntryPusherService;
 import net.floodlightcontroller.topology.ITopologyService;
 import net.floodlightcontroller.util.FlowModUtils;
+import net.floodlightcontroller.util.OFMessageUtils;
 
 /**
  * A simple load balancer module for ping, tcp, and udp flows. This module is accessed 
@@ -105,7 +106,7 @@ public class LoadBalancer implements IFloodlightModule,
     protected IDeviceService deviceManagerService;
     protected IRoutingService routingEngineService;
     protected ITopologyService topologyService;
-    protected IStaticFlowEntryPusherService sfpService;
+    protected IStaticEntryPusherService sfpService;
     protected IOFSwitchService switchService;
     
     protected HashMap<String, LBVip> vips;
@@ -127,8 +128,8 @@ public class LoadBalancer implements IFloodlightModule,
             new Comparator<SwitchPort>() {
                 @Override
                 public int compare(SwitchPort d1, SwitchPort d2) {
-                    DatapathId d1ClusterId = topologyService.getOpenflowDomainId(d1.getSwitchDPID());
-                    DatapathId d2ClusterId = topologyService.getOpenflowDomainId(d2.getSwitchDPID());
+                    DatapathId d1ClusterId = topologyService.getOpenflowDomainId(d1.getNodeId());
+                    DatapathId d2ClusterId = topologyService.getOpenflowDomainId(d2.getNodeId());
                     return d1ClusterId.compareTo(d2ClusterId);
                 }
             };
@@ -396,11 +397,11 @@ public class LoadBalancer implements IFloodlightModule,
         boolean on_same_island = false;
         boolean on_same_if = false;
         for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
-            DatapathId dstSwDpid = dstDap.getSwitchDPID();
+            DatapathId dstSwDpid = dstDap.getNodeId();
             DatapathId dstIsland = topologyService.getOpenflowDomainId(dstSwDpid);
             if ((dstIsland != null) && dstIsland.equals(srcIsland)) {
                 on_same_island = true;
-                if ((sw.getId().equals(dstSwDpid)) && ((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)).equals(dstDap.getPort()))) {
+                if ((sw.getId().equals(dstSwDpid)) && OFMessageUtils.getInPort(pi).equals(dstDap.getPortId())) {
                     on_same_if = true;
                 }
                 break;
@@ -441,9 +442,9 @@ public class LoadBalancer implements IFloodlightModule,
             SwitchPort srcDap = srcDaps[iSrcDaps];
             SwitchPort dstDap = dstDaps[iDstDaps];
             DatapathId srcCluster = 
-                    topologyService.getOpenflowDomainId(srcDap.getSwitchDPID());
+                    topologyService.getOpenflowDomainId(srcDap.getNodeId());
             DatapathId dstCluster = 
-                    topologyService.getOpenflowDomainId(dstDap.getSwitchDPID());
+                    topologyService.getOpenflowDomainId(dstDap.getNodeId());
 
             int srcVsDest = srcCluster.compareTo(dstCluster);
             if (srcVsDest == 0) {
@@ -451,15 +452,15 @@ public class LoadBalancer implements IFloodlightModule,
                         (srcCluster != null) && 
                         (dstCluster != null)) {
                     Route routeIn = 
-                            routingEngineService.getRoute(srcDap.getSwitchDPID(),
-                                                   srcDap.getPort(),
-                                                   dstDap.getSwitchDPID(),
-                                                   dstDap.getPort(), U64.of(0));
+                            routingEngineService.getRoute(srcDap.getNodeId(),
+                                                   srcDap.getPortId(),
+                                                   dstDap.getNodeId(),
+                                                   dstDap.getPortId(), U64.of(0));
                     Route routeOut = 
-                            routingEngineService.getRoute(dstDap.getSwitchDPID(),
-                                                   dstDap.getPort(),
-                                                   srcDap.getSwitchDPID(),
-                                                   srcDap.getPort(), U64.of(0));
+                            routingEngineService.getRoute(dstDap.getNodeId(),
+                                                   dstDap.getPortId(),
+                                                   srcDap.getNodeId(),
+                                                   srcDap.getPortId(), U64.of(0));
 
                     // use static flow entry pusher to push flow mod along in and out path
                     // in: match src client (ip, port), rewrite dest from vip ip/port to member ip/port, forward
@@ -814,7 +815,7 @@ public class LoadBalancer implements IFloodlightModule,
         l.add(IDebugCounterService.class);
         l.add(ITopologyService.class);
         l.add(IRoutingService.class);
-        l.add(IStaticFlowEntryPusherService.class);
+        l.add(IStaticEntryPusherService.class);
 
         return l;
     }
@@ -828,7 +829,7 @@ public class LoadBalancer implements IFloodlightModule,
         deviceManagerService = context.getServiceImpl(IDeviceService.class);
         routingEngineService = context.getServiceImpl(IRoutingService.class);
         topologyService = context.getServiceImpl(ITopologyService.class);
-        sfpService = context.getServiceImpl(IStaticFlowEntryPusherService.class);
+        sfpService = context.getServiceImpl(IStaticEntryPusherService.class);
         switchService = context.getServiceImpl(IOFSwitchService.class);
         
         vips = new HashMap<String, LBVip>();
diff --git a/src/main/java/net/floodlightcontroller/notification/INotificationManager.java b/src/main/java/net/floodlightcontroller/notification/INotificationManager.java
deleted file mode 100644
index afe2bbf4daebbedc784667afd16771ef0b2d455a..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/notification/INotificationManager.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package net.floodlightcontroller.notification;
-
-/**
- * Base interface for managing notifications.
- *
- * Notification is used to alert or inform notification receiver.
- * Notification can be a message written into log file or an SNMP trap or
- * SNMP notification.
- *
- * @author kevin.wang@bigswitch.com
- *
- */
-public interface INotificationManager {
-
-    /**
-     * Post a notification. Depending on the underline implementation, it
-     * may write the notes to log file or send an SNMP notification/trap.
-     *
-     * @param notes   string message to be sent to receiver
-     */
-    public void postNotification(String notes);
-
-}
diff --git a/src/main/java/net/floodlightcontroller/notification/INotificationManagerFactory.java b/src/main/java/net/floodlightcontroller/notification/INotificationManagerFactory.java
deleted file mode 100644
index 270fb26f47673a6b0d3b58d86c1ebc294ed47972..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/notification/INotificationManagerFactory.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.floodlightcontroller.notification;
-
-/**
- * This factory interface produce INotificationManager instance.
- *
- * @author kevin.wang@bigswitch.com
- *
- */
-public interface INotificationManagerFactory {
-
-    /**
-     * Produce and returns a NotificationManager based on the name
-     *
-     * @param clazz
-     * @return NotificationManager instance
-     */
-    <T> INotificationManager getNotificationManager(Class<T> clazz);
-}
diff --git a/src/main/java/net/floodlightcontroller/notification/NotificationManagerFactory.java b/src/main/java/net/floodlightcontroller/notification/NotificationManagerFactory.java
deleted file mode 100644
index 5fc1d4ae41f4180cb191b822173c0d75d43ef11f..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/notification/NotificationManagerFactory.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package net.floodlightcontroller.notification;
-
-import net.floodlightcontroller.notification.syslog.SyslogNotificationFactory;
-
-/**
- * This factory is a public utility to get NotificationManager
- * instance.
- *
- * @author kevinwang
- * @edited Ryan Izard, rizard@g.clemson.edu, ryan.izard@bigswich.com
- *
- */
-public class NotificationManagerFactory {
-
-    public static final String  NOTIFICATION_FACTORY_NAME =
-            "floodlight.notification.factoryName";
-
-    /**
-     * Do not set the default here. Delay until init(), which will be
-     * called by the JVM at class load. This will allow the unit tests
-     * to test dynamic binding to a factory, then reset to the default
-     * factory by clearing the System property and then calling init() 
-     * again for subsequent unit tests that actually need a non-mocked 
-     * NotificationManagerFactory.
-     * 
-     * If a dynamic binding is not specified, init() will fall through 
-     * to else and the default of SyslogNotifcationFactory will be used.
-     */
-    private static INotificationManagerFactory factory; 
-
-    /**
-     * Dynamically bind to a factory if there is one specified.
-     * This provides a simple and very basic implementation to override
-     * with a customized NotificationFactory.
-     *
-     */
-    static {
-        NotificationManagerFactory.init();
-    }
-
-    /**
-     * A simple mechanism to initialize factory with dynamic binding.
-     * 
-     * Extended to default to SyslogNotifcationFactory in the event
-     * a dynamic binding is not specified via System properties.
-     * This allows init() to be called multiple times for the unit tests
-     * and select the default or a another factory if the System property
-     * is cleared or is set, respectively.
-     */
-    protected static void init() {
-        String notificationfactoryClassName = null;
-        try {
-            notificationfactoryClassName =
-                    System.getProperty(NOTIFICATION_FACTORY_NAME);
-        } catch (SecurityException e) {
-            throw new RuntimeException(e);
-        }
-        if (notificationfactoryClassName != null) {
-            Class<?> nfc;
-            try {
-                nfc = Class.forName(notificationfactoryClassName);
-                factory = (INotificationManagerFactory) nfc.newInstance();
-            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
-                throw new RuntimeException(e);
-            }
-         } else {
-        	 factory = new SyslogNotificationFactory(); // use as the default
-         }
-    }
-
-    /**
-     * Helper method to create a NotificationManager instance by class
-     * with the underline factory
-     * @param clazz
-     * @return
-     */
-    public static <T> INotificationManager getNotificationManager(Class<T> clazz) {
-        return factory.getNotificationManager(clazz);
-    }
-
-    /**
-     * Helper method to return the factory
-     * @return the INotificationManagerFactory instance
-     */
-    public static <T> INotificationManagerFactory getNotificationManagerFactory() {
-        return factory;
-    }
-
-}
diff --git a/src/main/java/net/floodlightcontroller/notification/syslog/SyslogNotificationFactory.java b/src/main/java/net/floodlightcontroller/notification/syslog/SyslogNotificationFactory.java
deleted file mode 100644
index 62f7f976e17d5b962b619350b076170c9e34c231..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/notification/syslog/SyslogNotificationFactory.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.floodlightcontroller.notification.syslog;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.floodlightcontroller.notification.INotificationManager;
-import net.floodlightcontroller.notification.INotificationManagerFactory;
-
-public class SyslogNotificationFactory implements
-    INotificationManagerFactory {
-
-    @Override
-    public <T> INotificationManager getNotificationManager(Class<T> clazz) {
-        Logger logger = LoggerFactory.getLogger(clazz.getCanonicalName() + ".syslog.notification");
-        return new SyslogNotificationManager(logger);
-    }
-
-}
diff --git a/src/main/java/net/floodlightcontroller/notification/syslog/SyslogNotificationManager.java b/src/main/java/net/floodlightcontroller/notification/syslog/SyslogNotificationManager.java
deleted file mode 100644
index 5d5efdb4e0c9d7587a09df41c279bea4443f3ba3..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/notification/syslog/SyslogNotificationManager.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package net.floodlightcontroller.notification.syslog;
-
-import org.slf4j.Logger;
-
-import net.floodlightcontroller.notification.INotificationManager;
-
-public class SyslogNotificationManager implements INotificationManager {
-
-    private final Logger logger;
-
-    public SyslogNotificationManager(Logger logger) {
-        this.logger = logger;
-    }
-
-    @Override
-    public void postNotification(String notes) {
-        logger.warn(notes);
-    }
-
-}
diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
index 47594075d06b5dc9b055de854e45a779ec17cf3a..fa88fd069ca4c700fd41f2df999923b4504dd286 100644
--- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
+++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
@@ -17,7 +17,6 @@
 
 package net.floodlightcontroller.routing;
 
-import java.io.IOException;
 import java.util.EnumSet;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -64,6 +63,7 @@ import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
 import org.projectfloodlight.openflow.types.U64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -74,402 +74,400 @@ import org.slf4j.LoggerFactory;
  * decision.
  */
 public abstract class ForwardingBase implements IOFMessageListener {
-	protected static Logger log = LoggerFactory.getLogger(ForwardingBase.class);
-
-	protected static int OFMESSAGE_DAMPER_CAPACITY = 10000; // TODO: find sweet spot
-	protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
-
-	public static int FLOWMOD_DEFAULT_IDLE_TIMEOUT = 5; // in seconds
-	public static int FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
-	public static int FLOWMOD_DEFAULT_PRIORITY = 1; // 0 is the default table-miss flow in OF1.3+, so we need to use 1
-	
-	protected static boolean FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = false;
-		
-	protected static boolean FLOWMOD_DEFAULT_MATCH_VLAN = true;
-	protected static boolean FLOWMOD_DEFAULT_MATCH_MAC = true;
-	protected static boolean FLOWMOD_DEFAULT_MATCH_IP_ADDR = true;
-	protected static boolean FLOWMOD_DEFAULT_MATCH_TRANSPORT = true;
-
-	protected static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT_CONSTANT = 5;
-	protected static final short FLOWMOD_DEFAULT_HARD_TIMEOUT_CONSTANT = 0;
-	
-	protected static boolean FLOOD_ALL_ARP_PACKETS = false;
-	
-	protected static boolean REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = true;
-
-	protected IFloodlightProviderService floodlightProviderService;
-	protected IOFSwitchService switchService;
-	protected IDeviceService deviceManagerService;
-	protected IRoutingService routingEngineService;
-	protected ITopologyService topologyService;
-	protected IDebugCounterService debugCounterService;
-	protected ILinkDiscoveryService linkService;
-
-	protected OFMessageDamper messageDamper;
-
-	// for broadcast loop suppression
-	protected boolean broadcastCacheFeature = true;
-	public final int prime1 = 2633;  // for hash calculation
-	public final static int prime2 = 4357;  // for hash calculation
-	public TimedCache<Long> broadcastCache = new TimedCache<Long>(100, 5*1000);  // 5 seconds interval;
-
-	// flow-mod - for use in the cookie
-	public static final int FORWARDING_APP_ID = 2; // TODO: This must be managed
-	// by a global APP_ID class
-	static {
-		AppCookie.registerApp(FORWARDING_APP_ID, "Forwarding");
-	}
-	public static final U64 appCookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
-
-	// Comparator for sorting by SwitchCluster
-	public Comparator<SwitchPort> clusterIdComparator =
-			new Comparator<SwitchPort>() {
-		@Override
-		public int compare(SwitchPort d1, SwitchPort d2) {
-			DatapathId d1ClusterId = topologyService.getOpenflowDomainId(d1.getSwitchDPID());
-			DatapathId d2ClusterId = topologyService.getOpenflowDomainId(d2.getSwitchDPID());
-			return d1ClusterId.compareTo(d2ClusterId);
-		}
-	};
-
-	protected void init() {
-		messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
-				EnumSet.of(OFType.FLOW_MOD),
-				OFMESSAGE_DAMPER_TIMEOUT);
-	}
-
-	protected void startUp() {
-		floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this);
-	}
-
-	@Override
-	public String getName() {
-		return "forwarding";
-	}
-
-	/**
-	 * All subclasses must define this function if they want any specific
-	 * forwarding action
-	 *
-	 * @param sw
-	 *            Switch that the packet came in from
-	 * @param pi
-	 *            The packet that came in
-	 * @param decision
-	 *            Any decision made by a policy engine
-	 */
-	public abstract Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, 
-			IRoutingDecision decision, FloodlightContext cntx);
-
-	@Override
-	public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
-		switch (msg.getType()) {
-		case PACKET_IN:
-			IRoutingDecision decision = null;
-			if (cntx != null) {
-				decision = RoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
-			}
-
-			return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx);
-		default:
-			break;
-		}
-		return Command.CONTINUE;
-	}
-
-	/**
-	 * Push routes from back to front
-	 * @param route Route to push
-	 * @param match OpenFlow fields to match on
-	 * @param srcSwPort Source switch port for the first hop
-	 * @param dstSwPort Destination switch port for final hop
-	 * @param cookie The cookie to set in each flow_mod
-	 * @param cntx The floodlight context
-	 * @param requestFlowRemovedNotification if set to true then the switch would
-	 *        send a flow mod removal notification when the flow mod expires
-	 * @param flowModCommand flow mod. command to use, e.g. OFFlowMod.OFPFC_ADD,
-	 *        OFFlowMod.OFPFC_MODIFY etc.
-	 * @return true if a packet out was sent on the first-hop switch of this route
-	 */
-	public boolean pushRoute(Route route, Match match, OFPacketIn pi,
-			DatapathId pinSwitch, U64 cookie, FloodlightContext cntx,
-			boolean requestFlowRemovedNotification, OFFlowModCommand flowModCommand) {
-
-		boolean packetOutSent = false;
-
-		List<NodePortTuple> switchPortList = route.getPath();
-
-		for (int indx = switchPortList.size() - 1; indx > 0; indx -= 2) {
-			// indx and indx-1 will always have the same switch DPID.
-			DatapathId switchDPID = switchPortList.get(indx).getNodeId();
-			IOFSwitch sw = switchService.getSwitch(switchDPID);
-
-			if (sw == null) {
-				if (log.isWarnEnabled()) {
-					log.warn("Unable to push route, switch at DPID {} " + "not available", switchDPID);
-				}
-				return packetOutSent;
-			}
-			
-			// need to build flow mod based on what type it is. Cannot set command later
-			OFFlowMod.Builder fmb;
-			switch (flowModCommand) {
-			case ADD:
-				fmb = sw.getOFFactory().buildFlowAdd();
-				break;
-			case DELETE:
-				fmb = sw.getOFFactory().buildFlowDelete();
-				break;
-			case DELETE_STRICT:
-				fmb = sw.getOFFactory().buildFlowDeleteStrict();
-				break;
-			case MODIFY:
-				fmb = sw.getOFFactory().buildFlowModify();
-				break;
-			default:
-				log.error("Could not decode OFFlowModCommand. Using MODIFY_STRICT. (Should another be used as the default?)");        
-			case MODIFY_STRICT:
-				fmb = sw.getOFFactory().buildFlowModifyStrict();
-				break;			
-			}
-			
-			OFActionOutput.Builder aob = sw.getOFFactory().actions().buildOutput();
-			List<OFAction> actions = new ArrayList<OFAction>();	
- 			Match.Builder mb = MatchUtils.convertToVersion(match, sw.getOFFactory().getVersion());
- 			
-			// set input and output ports on the switch
-			OFPort outPort = switchPortList.get(indx).getPortId();
-			OFPort inPort = switchPortList.get(indx - 1).getPortId();
-			mb.setExact(MatchField.IN_PORT, inPort);
-			aob.setPort(outPort);
-			aob.setMaxLen(Integer.MAX_VALUE);
-			actions.add(aob.build());
-			
-			if (FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG || requestFlowRemovedNotification) {
-				Set<OFFlowModFlags> flags = new HashSet<>();
-				flags.add(OFFlowModFlags.SEND_FLOW_REM);
-				fmb.setFlags(flags);
-			}
-			
-			fmb.setMatch(mb.build())
-			.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
-			.setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
-			.setBufferId(OFBufferId.NO_BUFFER)
-			.setCookie(cookie)
-			.setOutPort(outPort)
-			.setPriority(FLOWMOD_DEFAULT_PRIORITY);
-			
-			FlowModUtils.setActions(fmb, actions, sw);
-			
-			try {
-				if (log.isTraceEnabled()) {
-					log.trace("Pushing Route flowmod routeIndx={} " +
-							"sw={} inPort={} outPort={}",
-							new Object[] {indx,
-							sw,
-							fmb.getMatch().get(MatchField.IN_PORT),
-							outPort });
-				}
-				
-				if (OFDPAUtils.isOFDPASwitch(sw)) {
-					OFDPAUtils.addLearningSwitchFlow(sw, cookie, 
-							FLOWMOD_DEFAULT_PRIORITY, 
-							FLOWMOD_DEFAULT_HARD_TIMEOUT,
-							FLOWMOD_DEFAULT_IDLE_TIMEOUT,
-							fmb.getMatch(), 
-							null, // TODO how to determine output VLAN for lookup of L2 interface group
-							outPort);
-				} else {
-					messageDamper.write(sw, fmb.build());
-				}
-
-				/* Push the packet out the first hop switch */
-				if (sw.getId().equals(pinSwitch) &&
-						!fmb.getCommand().equals(OFFlowModCommand.DELETE) &&
-						!fmb.getCommand().equals(OFFlowModCommand.DELETE_STRICT)) {
-					/* Use the buffered packet at the switch, if there's one stored */
-					pushPacket(sw, pi, outPort, true, cntx);
-					packetOutSent = true;
-				}
-			} catch (IOException e) {
-				log.error("Failure writing flow mod", e);
-			}
-		}
-
-		return packetOutSent;
-	}
-	
-	/**
-	 * Pushes a packet-out to a switch. The assumption here is that
-	 * the packet-in was also generated from the same switch. Thus, if the input
-	 * port of the packet-in and the outport are the same, the function will not
-	 * push the packet-out.
-	 * @param sw switch that generated the packet-in, and from which packet-out is sent
-	 * @param pi packet-in
-	 * @param outport output port
-	 * @param useBufferedPacket use the packet buffered at the switch, if possible
-	 * @param cntx context of the packet
-	 */
-	protected void pushPacket(IOFSwitch sw, OFPacketIn pi, OFPort outport, boolean useBufferedPacket, FloodlightContext cntx) {
-		if (pi == null) {
-			return;
-		}
-
-		// The assumption here is (sw) is the switch that generated the
-		// packet-in. If the input port is the same as output port, then
-		// the packet-out should be ignored.
-		if ((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)).equals(outport)) {
-			if (log.isDebugEnabled()) {
-				log.debug("Attempting to do packet-out to the same " +
-						"interface as packet-in. Dropping packet. " +
-						" SrcSwitch={}, pi={}",
-						new Object[]{sw, pi});
-				return;
-			}
-		}
-
-		if (log.isTraceEnabled()) {
-			log.trace("PacketOut srcSwitch={} pi={}",
-					new Object[] {sw, pi});
-		}
-
-		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
-		List<OFAction> actions = new ArrayList<OFAction>();
-		actions.add(sw.getOFFactory().actions().output(outport, Integer.MAX_VALUE));
-		pob.setActions(actions);
-
-		/* Use packet in buffer if there is a buffer ID set */
-		if (useBufferedPacket) {
-			pob.setBufferId(pi.getBufferId()); /* will be NO_BUFFER if there isn't one */
-		} else {
-			pob.setBufferId(OFBufferId.NO_BUFFER);
-		}
-
-		if (pob.getBufferId().equals(OFBufferId.NO_BUFFER)) {
-			byte[] packetData = pi.getData();
-			pob.setData(packetData);
-		}
-
-		pob.setInPort((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)));
-
-		try {
-			messageDamper.write(sw, pob.build());
-		} catch (IOException e) {
-			log.error("Failure writing packet out", e);
-		}
-	}
-
-	/**
-	 * Write packetout message to sw with output actions to one or more
-	 * output ports with inPort/outPorts passed in.
-	 * @param packetData
-	 * @param sw
-	 * @param inPort
-	 * @param ports
-	 * @param cntx
-	 */
-	public void packetOutMultiPort(byte[] packetData, IOFSwitch sw, 
-			OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
-		//setting actions
-		List<OFAction> actions = new ArrayList<OFAction>();
-
-		Iterator<OFPort> j = outPorts.iterator();
-
-		while (j.hasNext()) {
-			actions.add(sw.getOFFactory().actions().output(j.next(), 0));
-		}
-
-		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
-		pob.setActions(actions);
-
-		pob.setBufferId(OFBufferId.NO_BUFFER);
-		pob.setInPort(inPort);
-
-		pob.setData(packetData);
-
-		try {
-			if (log.isTraceEnabled()) {
-				log.trace("write broadcast packet on switch-id={} " +
-						"interfaces={} packet-out={}",
-						new Object[] {sw.getId(), outPorts, pob.build()});
-			}
-			messageDamper.write(sw, pob.build());
-
-		} catch (IOException e) {
-			log.error("Failure writing packet out", e);
-		}
-	}
-
-	/**
-	 * @see packetOutMultiPort
-	 * Accepts a PacketIn instead of raw packet data. Note that the inPort
-	 * and switch can be different than the packet in switch/port
-	 */
-	public void packetOutMultiPort(OFPacketIn pi, IOFSwitch sw,
-			OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
-		packetOutMultiPort(pi.getData(), sw, inPort, outPorts, cntx);
-	}
-
-	/**
-	 * @see packetOutMultiPort
-	 * Accepts an IPacket instead of raw packet data. Note that the inPort
-	 * and switch can be different than the packet in switch/port
-	 */
-	public void packetOutMultiPort(IPacket packet, IOFSwitch sw,
-			OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
-		packetOutMultiPort(packet.serialize(), sw, inPort, outPorts, cntx);
-	}
-
-	public static boolean blockHost(IOFSwitchService switchService,
-			SwitchPort sw_tup, MacAddress host_mac, short hardTimeout, U64 cookie) {
-
-		if (sw_tup == null) {
-			return false;
-		}
-
-		IOFSwitch sw = switchService.getSwitch(sw_tup.getSwitchDPID());
-		if (sw == null) {
-			return false;
-		}
-
-		OFPort inputPort = sw_tup.getPort();
-		log.debug("blockHost sw={} port={} mac={}",
-				new Object[] { sw, sw_tup.getPort(), host_mac.getLong() });
-
-		// Create flow-mod based on packet-in and src-switch
-		OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd();
-
-		Match.Builder mb = sw.getOFFactory().buildMatch();
-		List<OFAction> actions = new ArrayList<OFAction>(); // Set no action to drop
-		mb.setExact(MatchField.IN_PORT, inputPort);
-		if (host_mac.getLong() != -1L) {
-			mb.setExact(MatchField.ETH_SRC, host_mac);
-		}
-
-		fmb.setCookie(cookie)
-		.setHardTimeout(hardTimeout)
-		.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
-		.setPriority(FLOWMOD_DEFAULT_PRIORITY)
-		.setBufferId(OFBufferId.NO_BUFFER)
-		.setMatch(mb.build());
-		
-		FlowModUtils.setActions(fmb, actions, sw);
-
-		log.debug("write drop flow-mod sw={} match={} flow-mod={}",
-					new Object[] { sw, mb.build(), fmb.build() });
-		// TODO: can't use the message damper since this method is static
-		sw.write(fmb.build());
-		
-		return true;
-	}
-
-	@Override
-	public boolean isCallbackOrderingPrereq(OFType type, String name) {
-		return (type.equals(OFType.PACKET_IN) && (name.equals("topology") || name.equals("devicemanager")));
-	}
-
-	@Override
-	public boolean isCallbackOrderingPostreq(OFType type, String name) {
-		return false;
-	}
+    protected static Logger log = LoggerFactory.getLogger(ForwardingBase.class);
+
+    protected static int OFMESSAGE_DAMPER_CAPACITY = 10000; // TODO: find sweet spot
+    protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
+
+    public static int FLOWMOD_DEFAULT_IDLE_TIMEOUT = 5; // in seconds
+    public static int FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
+    public static int FLOWMOD_DEFAULT_PRIORITY = 1; // 0 is the default table-miss flow in OF1.3+, so we need to use 1
+
+    public static TableId FLOWMOD_DEFAULT_TABLE_ID = TableId.ZERO;
+
+    protected static boolean FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = false;
+
+    protected static boolean FLOWMOD_DEFAULT_MATCH_VLAN = true;
+    protected static boolean FLOWMOD_DEFAULT_MATCH_MAC = true;
+    protected static boolean FLOWMOD_DEFAULT_MATCH_IP_ADDR = true;
+    protected static boolean FLOWMOD_DEFAULT_MATCH_TRANSPORT = true;
+
+    protected static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT_CONSTANT = 5;
+    protected static final short FLOWMOD_DEFAULT_HARD_TIMEOUT_CONSTANT = 0;
+
+    protected static boolean FLOOD_ALL_ARP_PACKETS = false;
+
+    protected static boolean REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = true;
+
+    protected IFloodlightProviderService floodlightProviderService;
+    protected IOFSwitchService switchService;
+    protected IDeviceService deviceManagerService;
+    protected IRoutingService routingEngineService;
+    protected ITopologyService topologyService;
+    protected IDebugCounterService debugCounterService;
+    protected ILinkDiscoveryService linkService;
+
+    protected OFMessageDamper messageDamper;
+
+    // for broadcast loop suppression
+    protected boolean broadcastCacheFeature = true;
+    public final int prime1 = 2633;  // for hash calculation
+    public final static int prime2 = 4357;  // for hash calculation
+    public TimedCache<Long> broadcastCache = new TimedCache<Long>(100, 5*1000);  // 5 seconds interval;
+
+    // flow-mod - for use in the cookie
+    public static final int FORWARDING_APP_ID = 2; // TODO: This must be managed
+    // by a global APP_ID class
+    static {
+        AppCookie.registerApp(FORWARDING_APP_ID, "Forwarding");
+    }
+    public static final U64 appCookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
+
+    // Comparator for sorting by SwitchCluster
+    public Comparator<SwitchPort> clusterIdComparator =
+            new Comparator<SwitchPort>() {
+        @Override
+        public int compare(SwitchPort d1, SwitchPort d2) {
+            DatapathId d1ClusterId = topologyService.getOpenflowDomainId(d1.getNodeId());
+            DatapathId d2ClusterId = topologyService.getOpenflowDomainId(d2.getNodeId());
+            return d1ClusterId.compareTo(d2ClusterId);
+        }
+    };
+
+    protected void init() {
+        messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
+                EnumSet.of(OFType.FLOW_MOD),
+                OFMESSAGE_DAMPER_TIMEOUT);
+    }
+
+    protected void startUp() {
+        floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this);
+    }
+
+    @Override
+    public String getName() {
+        return "forwarding";
+    }
+
+    /**
+     * All subclasses must define this function if they want any specific
+     * forwarding action
+     *
+     * @param sw
+     *            Switch that the packet came in from
+     * @param pi
+     *            The packet that came in
+     * @param decision
+     *            Any decision made by a policy engine
+     */
+    public abstract Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, 
+            IRoutingDecision decision, FloodlightContext cntx);
+
+    @Override
+    public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+        switch (msg.getType()) {
+        case PACKET_IN:
+            IRoutingDecision decision = null;
+            if (cntx != null) {
+                decision = RoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
+            }
+
+            return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx);
+        default:
+            break;
+        }
+        return Command.CONTINUE;
+    }
+
+    /**
+     * Push routes from back to front
+     * @param route Route to push
+     * @param match OpenFlow fields to match on
+     * @param srcSwPort Source switch port for the first hop
+     * @param dstSwPort Destination switch port for final hop
+     * @param cookie The cookie to set in each flow_mod
+     * @param cntx The floodlight context
+     * @param requestFlowRemovedNotification if set to true then the switch would
+     *        send a flow mod removal notification when the flow mod expires
+     * @param flowModCommand flow mod. command to use, e.g. OFFlowMod.OFPFC_ADD,
+     *        OFFlowMod.OFPFC_MODIFY etc.
+     * @return true if a packet out was sent on the first-hop switch of this route
+     */
+    public boolean pushRoute(Route route, Match match, OFPacketIn pi,
+            DatapathId pinSwitch, U64 cookie, FloodlightContext cntx,
+            boolean requestFlowRemovedNotification, OFFlowModCommand flowModCommand) {
+
+        boolean packetOutSent = false;
+
+        List<NodePortTuple> switchPortList = route.getPath();
+
+        for (int indx = switchPortList.size() - 1; indx > 0; indx -= 2) {
+            // indx and indx-1 will always have the same switch DPID.
+            DatapathId switchDPID = switchPortList.get(indx).getNodeId();
+            IOFSwitch sw = switchService.getSwitch(switchDPID);
+
+            if (sw == null) {
+                if (log.isWarnEnabled()) {
+                    log.warn("Unable to push route, switch at DPID {} " + "not available", switchDPID);
+                }
+                return packetOutSent;
+            }
+
+            // need to build flow mod based on what type it is. Cannot set command later
+            OFFlowMod.Builder fmb;
+            switch (flowModCommand) {
+            case ADD:
+                fmb = sw.getOFFactory().buildFlowAdd();
+                break;
+            case DELETE:
+                fmb = sw.getOFFactory().buildFlowDelete();
+                break;
+            case DELETE_STRICT:
+                fmb = sw.getOFFactory().buildFlowDeleteStrict();
+                break;
+            case MODIFY:
+                fmb = sw.getOFFactory().buildFlowModify();
+                break;
+            default:
+                log.error("Could not decode OFFlowModCommand. Using MODIFY_STRICT. (Should another be used as the default?)");        
+            case MODIFY_STRICT:
+                fmb = sw.getOFFactory().buildFlowModifyStrict();
+                break;			
+            }
+
+            OFActionOutput.Builder aob = sw.getOFFactory().actions().buildOutput();
+            List<OFAction> actions = new ArrayList<OFAction>();	
+            Match.Builder mb = MatchUtils.convertToVersion(match, sw.getOFFactory().getVersion());
+
+            // set input and output ports on the switch
+            OFPort outPort = switchPortList.get(indx).getPortId();
+            OFPort inPort = switchPortList.get(indx - 1).getPortId();
+            mb.setExact(MatchField.IN_PORT, inPort);
+            aob.setPort(outPort);
+            aob.setMaxLen(Integer.MAX_VALUE);
+            actions.add(aob.build());
+
+            if (FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG || requestFlowRemovedNotification) {
+                Set<OFFlowModFlags> flags = new HashSet<>();
+                flags.add(OFFlowModFlags.SEND_FLOW_REM);
+                fmb.setFlags(flags);
+            }
+
+            fmb.setMatch(mb.build())
+            .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+            .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+            .setBufferId(OFBufferId.NO_BUFFER)
+            .setCookie(cookie)
+            .setOutPort(outPort)
+            .setPriority(FLOWMOD_DEFAULT_PRIORITY);
+
+            FlowModUtils.setActions(fmb, actions, sw);
+
+            /* Configure for particular switch pipeline */
+            if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_10) != 0) {
+                fmb.setTableId(FLOWMOD_DEFAULT_TABLE_ID);
+            }
+            
+            if (log.isTraceEnabled()) {
+                log.trace("Pushing Route flowmod routeIndx={} " +
+                        "sw={} inPort={} outPort={}",
+                        new Object[] {indx,
+                                sw,
+                                fmb.getMatch().get(MatchField.IN_PORT),
+                                outPort });
+            }
+
+            if (OFDPAUtils.isOFDPASwitch(sw)) {
+                OFDPAUtils.addLearningSwitchFlow(sw, cookie, 
+                        FLOWMOD_DEFAULT_PRIORITY, 
+                        FLOWMOD_DEFAULT_HARD_TIMEOUT,
+                        FLOWMOD_DEFAULT_IDLE_TIMEOUT,
+                        fmb.getMatch(), 
+                        null, // TODO how to determine output VLAN for lookup of L2 interface group
+                        outPort);
+            } else {
+                messageDamper.write(sw, fmb.build());
+            }
+
+            /* Push the packet out the first hop switch */
+            if (sw.getId().equals(pinSwitch) &&
+                    !fmb.getCommand().equals(OFFlowModCommand.DELETE) &&
+                    !fmb.getCommand().equals(OFFlowModCommand.DELETE_STRICT)) {
+                /* Use the buffered packet at the switch, if there's one stored */
+                pushPacket(sw, pi, outPort, true, cntx);
+                packetOutSent = true;
+            }
+        }
+
+        return packetOutSent;
+    }
+
+    /**
+     * Pushes a packet-out to a switch. The assumption here is that
+     * the packet-in was also generated from the same switch. Thus, if the input
+     * port of the packet-in and the outport are the same, the function will not
+     * push the packet-out.
+     * @param sw switch that generated the packet-in, and from which packet-out is sent
+     * @param pi packet-in
+     * @param outport output port
+     * @param useBufferedPacket use the packet buffered at the switch, if possible
+     * @param cntx context of the packet
+     */
+    protected void pushPacket(IOFSwitch sw, OFPacketIn pi, OFPort outport, boolean useBufferedPacket, FloodlightContext cntx) {
+        if (pi == null) {
+            return;
+        }
+
+        // The assumption here is (sw) is the switch that generated the
+        // packet-in. If the input port is the same as output port, then
+        // the packet-out should be ignored.
+        if ((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)).equals(outport)) {
+            if (log.isDebugEnabled()) {
+                log.debug("Attempting to do packet-out to the same " +
+                        "interface as packet-in. Dropping packet. " +
+                        " SrcSwitch={}, pi={}",
+                        new Object[]{sw, pi});
+                return;
+            }
+        }
+
+        if (log.isTraceEnabled()) {
+            log.trace("PacketOut srcSwitch={} pi={}",
+                    new Object[] {sw, pi});
+        }
+
+        OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
+        List<OFAction> actions = new ArrayList<OFAction>();
+        actions.add(sw.getOFFactory().actions().output(outport, Integer.MAX_VALUE));
+        pob.setActions(actions);
+
+        /* Use packet in buffer if there is a buffer ID set */
+        if (useBufferedPacket) {
+            pob.setBufferId(pi.getBufferId()); /* will be NO_BUFFER if there isn't one */
+        } else {
+            pob.setBufferId(OFBufferId.NO_BUFFER);
+        }
+
+        if (pob.getBufferId().equals(OFBufferId.NO_BUFFER)) {
+            byte[] packetData = pi.getData();
+            pob.setData(packetData);
+        }
+
+        pob.setInPort((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)));
+
+        messageDamper.write(sw, pob.build());
+    }
+
+    /**
+     * Write packetout message to sw with output actions to one or more
+     * output ports with inPort/outPorts passed in.
+     * @param packetData
+     * @param sw
+     * @param inPort
+     * @param ports
+     * @param cntx
+     */
+    public void packetOutMultiPort(byte[] packetData, IOFSwitch sw, 
+            OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
+        //setting actions
+        List<OFAction> actions = new ArrayList<OFAction>();
+
+        Iterator<OFPort> j = outPorts.iterator();
+
+        while (j.hasNext()) {
+            actions.add(sw.getOFFactory().actions().output(j.next(), 0));
+        }
+
+        OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
+        pob.setActions(actions);
+
+        pob.setBufferId(OFBufferId.NO_BUFFER);
+        pob.setInPort(inPort);
+
+        pob.setData(packetData);
+
+        if (log.isTraceEnabled()) {
+            log.trace("write broadcast packet on switch-id={} " +
+                    "interfaces={} packet-out={}",
+                    new Object[] {sw.getId(), outPorts, pob.build()});
+        }
+        messageDamper.write(sw, pob.build());
+    }
+
+    /**
+     * @see packetOutMultiPort
+     * Accepts a PacketIn instead of raw packet data. Note that the inPort
+     * and switch can be different than the packet in switch/port
+     */
+    public void packetOutMultiPort(OFPacketIn pi, IOFSwitch sw,
+            OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
+        packetOutMultiPort(pi.getData(), sw, inPort, outPorts, cntx);
+    }
+
+    /**
+     * @see packetOutMultiPort
+     * Accepts an IPacket instead of raw packet data. Note that the inPort
+     * and switch can be different than the packet in switch/port
+     */
+    public void packetOutMultiPort(IPacket packet, IOFSwitch sw,
+            OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
+        packetOutMultiPort(packet.serialize(), sw, inPort, outPorts, cntx);
+    }
+
+    public boolean blockHost(IOFSwitchService switchService,
+            SwitchPort sw_tup, MacAddress host_mac, short hardTimeout, U64 cookie) {
+
+        if (sw_tup == null) {
+            return false;
+        }
+
+        IOFSwitch sw = switchService.getSwitch(sw_tup.getNodeId());
+        if (sw == null) {
+            return false;
+        }
+
+        OFPort inputPort = sw_tup.getPortId();
+        if (log.isDebugEnabled()) {
+            log.debug("blockHost sw={} port={} mac={}",
+                    new Object[] { sw, sw_tup.getPortId(), host_mac.getLong() });
+        }
+
+        // Create flow-mod based on packet-in and src-switch
+        OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd();
+
+        Match.Builder mb = sw.getOFFactory().buildMatch();
+        List<OFAction> actions = new ArrayList<OFAction>(); // Set no action to drop
+        mb.setExact(MatchField.IN_PORT, inputPort);
+        if (host_mac.getLong() != -1L) {
+            mb.setExact(MatchField.ETH_SRC, host_mac);
+        }
+
+        fmb.setCookie(cookie)
+        .setHardTimeout(hardTimeout)
+        .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+        .setPriority(FLOWMOD_DEFAULT_PRIORITY)
+        .setBufferId(OFBufferId.NO_BUFFER)
+        .setMatch(mb.build());
+
+        FlowModUtils.setActions(fmb, actions, sw);
+
+        if (log.isDebugEnabled()) {
+            log.debug("write drop flow-mod sw={} match={} flow-mod={}",
+                    new Object[] { sw, mb.build(), fmb.build() });
+        }
+
+        messageDamper.write(sw, fmb.build());
+
+        return true;
+    }
+
+    @Override
+    public boolean isCallbackOrderingPrereq(OFType type, String name) {
+        return (type.equals(OFType.PACKET_IN) && (name.equals("topology") || name.equals("devicemanager")));
+    }
+
+    @Override
+    public boolean isCallbackOrderingPostreq(OFType type, String name) {
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/staticentry/FlowModInfo.java b/src/main/java/net/floodlightcontroller/staticentry/FlowModInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e4c0da20d2c055c5a645787033a6eaa9b1bf527
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/staticentry/FlowModInfo.java
@@ -0,0 +1,75 @@
+package net.floodlightcontroller.staticentry;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import net.floodlightcontroller.util.MatchUtils;
+
+import org.projectfloodlight.openflow.protocol.OFActionType;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActions;
+import org.projectfloodlight.openflow.protocol.actionid.OFActionId;
+import org.projectfloodlight.openflow.protocol.actionid.OFActionIdExperimenter;
+import org.projectfloodlight.openflow.protocol.actionid.OFActionIds;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
+import org.projectfloodlight.openflow.protocol.ver10.OFActionsVer10;
+import org.projectfloodlight.openflow.types.OFValueType;
+
+import com.google.common.collect.ImmutableSet;
+
+public class FlowModInfo {
+	private static final Set<FlowMatchProperty> matches = new HashSet<FlowMatchProperty>();
+	private static final Set<FlowActionProperty> actions = new HashSet<FlowActionProperty>();
+	//private static final Set<FlowInstructionProperty> instructions = new HashSet<FlowInstructionProperty>();
+	
+	private FlowModInfo() {}
+	private static volatile FlowModInfo instance = null;
+	public static synchronized FlowModInfo getInstance() {
+		if (instance == null) {
+			instance = new FlowModInfo();
+		}
+				
+		for (MatchFields m : MatchFields.values()) {
+			FlowMatchProperty fp = new FlowMatchProperty();
+			fp.property = MatchUtils.getMatchField(m);
+			fp.supportedOFVersions = ImmutableSet.copyOf(MatchUtils.getSupportedOFVersions(m));
+			fp.usageNotes = ImmutableSet.of("add-in-usage-strings!");
+			matches.add(fp);
+		}
+		for (OFActionType a : OFActionType.values()) {
+			FlowActionProperty fp = new FlowActionProperty();
+			//fp.property = a.g
+		}
+		
+		return instance;
+	}
+	
+	public static String toJson() {
+		return "";
+	}
+	
+	enum FP_VALUE_TYPE {
+		NONE,
+		NUMBER,
+		MAC_ADDR,
+		IPV4_ADDR,
+		IPV4_ADDR_W_MASK,
+		IPV6_ADDR,
+		IPV6_ADDR_W_MASK,
+		RESERVED_PORT,
+		
+	}
+	private static class FlowMatchProperty {
+		private Set<String> usageNotes;
+		private MatchField<?> property;
+		private Set<OFVersion> supportedOFVersions;
+	}
+	private static class FlowActionProperty {
+		private Set<String> usageNotes;
+		private OFActionId property;
+		private Set<OFVersion> supportedOFVersions;
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java b/src/main/java/net/floodlightcontroller/staticentry/IStaticEntryPusherService.java
similarity index 56%
rename from src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java
rename to src/main/java/net/floodlightcontroller/staticentry/IStaticEntryPusherService.java
index 0abf9d2f39ffdd3320ae64e0a1156bacb2cf3b98..b113841002b26d999002a158da37fe61450b0f6e 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/IStaticFlowEntryPusherService.java
+++ b/src/main/java/net/floodlightcontroller/staticentry/IStaticEntryPusherService.java
@@ -14,16 +14,18 @@
  *    under the License.
  **/
 
-package net.floodlightcontroller.staticflowentry;
+package net.floodlightcontroller.staticentry;
 
 import java.util.Map;
 
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.types.DatapathId;
 
 import net.floodlightcontroller.core.module.IFloodlightService;
 
-public interface IStaticFlowEntryPusherService extends IFloodlightService {
+public interface IStaticEntryPusherService extends IFloodlightService {
     /**
      * Adds a static flow.
      * @param name Name of the flow mod. Must be unique.
@@ -33,30 +35,38 @@ public interface IStaticFlowEntryPusherService extends IFloodlightService {
     public void addFlow(String name, OFFlowMod fm, DatapathId swDpid);
     
     /**
-     * Deletes a static flow
+     * Adds a static group.
+     * @param name Name of the group mod. Must be unique.
+     * @param gm The group to push.
+     * @param swDpid The switch DPID to push it to, in 00:00:00:00:00:00:00:01 notation.
+     */
+    public void addGroup(String name, OFGroupMod gm, DatapathId swDpid);
+    
+    /**
+     * Deletes a static flow or group entry
      * @param name The name of the static flow to delete.
      */
-    public void deleteFlow(String name);
+    public void deleteEntry(String name);
     
     /**
-     * Deletes all static flows for a particular switch
+     * Deletes all static flows and groups for a particular switch
      * @param dpid The DPID of the switch to delete flows for.
      */
-    public void deleteFlowsForSwitch(DatapathId dpid);
+    public void deleteEntriesForSwitch(DatapathId dpid);
     
     /**
-     * Deletes all flows.
+     * Deletes all flows and groups.
      */
-    public void deleteAllFlows();
+    public void deleteAllEntries();
     
     /**
-     * Gets all list of all flows
+     * Gets all list of all flows and groups
      */
-    public Map<String, Map<String, OFFlowMod>> getFlows();
+    public Map<String, Map<String, OFMessage>> getEntries();
     
     /**
-     * Gets a list of flows by switch
+     * Gets a list of flows and groups by switch
      */
-    public Map<String, OFFlowMod> getFlows(DatapathId dpid);
+    public Map<String, OFMessage> getEntries(DatapathId dpid);
 
 }
diff --git a/src/main/java/net/floodlightcontroller/staticentry/StaticEntries.java b/src/main/java/net/floodlightcontroller/staticentry/StaticEntries.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e83832b78e9ff3ad02bf2562336092f79f7cf99
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/staticentry/StaticEntries.java
@@ -0,0 +1,348 @@
+/**
+ *    Copyright 2013, Big Switch Networks, Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.staticentry;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import net.floodlightcontroller.core.util.AppCookie;
+import net.floodlightcontroller.staticentry.web.StaticEntryPusherResource;
+import net.floodlightcontroller.util.ActionUtils;
+import net.floodlightcontroller.util.GroupUtils;
+import net.floodlightcontroller.util.InstructionUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.MappingJsonFactory;
+
+import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+import org.projectfloodlight.openflow.protocol.OFInstructionType;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionClearActions;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionExperimenter;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionMeter;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionStatTrigger;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteActions;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteMetadata;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.U64;
+
+/**
+ * Represents static entries to be maintained by the controller on the 
+ * switches. 
+ */
+public class StaticEntries {
+	protected static Logger log = LoggerFactory.getLogger(StaticEntries.class);
+	private static final int INFINITE_TIMEOUT = 0;
+
+	/**
+	 * This function generates a random hash for the bottom half of the cookie
+	 * 
+	 * @param fm
+	 * @param userCookie
+	 * @param name
+	 * @return A cookie that encodes the application ID and a hash
+	 */
+	public static U64 computeEntryCookie(int userCookie, String name) {
+		// flow-specific hash is next 20 bits
+		int prime = 211;
+		int hash = 2311;
+		for (int i = 0; i < name.length(); i++) {
+			hash = hash * prime + (int) name.charAt(i);
+		}
+
+		return AppCookie.makeCookie(StaticEntryPusher.STATIC_ENTRY_APP_ID, hash);
+	}
+
+	/**
+	 * Sets defaults for an OFFlowMod used in the StaticFlowEntryPusher
+	 * @param fm The OFFlowMod to set defaults for
+	 * @param entryName The name of the entry. Used to compute the cookie.
+	 */
+	public static void initDefaultFlowMod(OFFlowMod.Builder fmb, String entryName) {
+		fmb.setIdleTimeout(INFINITE_TIMEOUT) // not setting these would also work
+		.setHardTimeout(INFINITE_TIMEOUT)
+		.setBufferId(OFBufferId.NO_BUFFER)
+		.setOutPort(OFPort.ANY) 
+		.setCookie(computeEntryCookie(0, entryName))
+		.setPriority(Integer.MAX_VALUE)
+		.setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM));
+		return;
+	}
+
+	/**
+	 * Gets the entry name of a message
+	 * @param fmJson The OFFlowMod in a JSON representation
+	 * @return The name of the OFFlowMod, null if not found
+	 * @throws IOException If there was an error parsing the JSON
+	 */
+	public static String getEntryNameFromJson(String fmJson) throws IOException{
+		MappingJsonFactory f = new MappingJsonFactory();
+		JsonParser jp;
+
+		try {
+			jp = f.createParser(fmJson);
+		} catch (JsonParseException e) {
+			throw new IOException(e);
+		}
+
+		jp.nextToken();
+		if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+			throw new IOException("Expected START_OBJECT");
+		}
+
+		while (jp.nextToken() != JsonToken.END_OBJECT) {
+			if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
+				throw new IOException("Expected FIELD_NAME");
+			}
+
+			String n = jp.getCurrentName();
+			jp.nextToken();
+			if (jp.getText().equals("")) 
+				continue;
+
+			if (n == StaticEntryPusher.Columns.COLUMN_NAME)
+				return jp.getText();
+		}
+		return null;
+	}
+	
+	public static Map<String, Object> groupModToStorageEntry(OFGroupMod gm, String sw, String name) {
+		Map<String, Object> entry = new HashMap<String, Object>();
+		entry.put(StaticEntryPusher.Columns.COLUMN_NAME, name);
+		entry.put(StaticEntryPusher.Columns.COLUMN_SWITCH, sw);
+		entry.put(StaticEntryPusher.Columns.COLUMN_ACTIVE, Boolean.toString(true));
+		
+		entry.put(StaticEntryPusher.Columns.COLUMN_GROUP_ID, Integer.toString(gm.getGroup().getGroupNumber()));
+		entry.put(StaticEntryPusher.Columns.COLUMN_GROUP_TYPE, GroupUtils.groupTypeToString(gm.getGroupType()));
+		entry.put(StaticEntryPusher.Columns.COLUMN_GROUP_BUCKETS, GroupUtils.groupBucketsToJsonArray(gm.getBuckets()));
+		
+		return entry;
+	}
+
+	/**
+	 * Parses an OFFlowMod (and it's inner Match) to the storage entry format.
+	 * @param fm The FlowMod to parse
+	 * @param sw The switch the FlowMod is going to be installed on
+	 * @param name The name of this static flow entry
+	 * @return A Map representation of the storage entry 
+	 */
+	public static Map<String, Object> flowModToStorageEntry(OFFlowMod fm, String sw, String name) throws Exception {
+		Map<String, Object> entry = new HashMap<String, Object>();
+		entry.put(StaticEntryPusher.Columns.COLUMN_NAME, name);
+		entry.put(StaticEntryPusher.Columns.COLUMN_SWITCH, sw);
+		entry.put(StaticEntryPusher.Columns.COLUMN_ACTIVE, Boolean.toString(true));
+		entry.put(StaticEntryPusher.Columns.COLUMN_PRIORITY, Integer.toString(fm.getPriority()));
+		entry.put(StaticEntryPusher.Columns.COLUMN_IDLE_TIMEOUT, Integer.toString(fm.getIdleTimeout()));
+		entry.put(StaticEntryPusher.Columns.COLUMN_HARD_TIMEOUT, Integer.toString(fm.getHardTimeout()));
+
+		switch (fm.getVersion()) {
+		case OF_10:
+			if (fm.getActions() != null) {
+				entry.put(StaticEntryPusher.Columns.COLUMN_ACTIONS, ActionUtils.actionsToString(fm.getActions()));
+			}
+			break;
+		case OF_11:
+		case OF_12:
+		case OF_13:
+		case OF_14:
+		case OF_15:
+			/* should have a table ID present */
+			if (fm.getTableId() != null) { /* if not set, then don't worry about it. Default will be set when built and sent to switch */
+				entry.put(StaticEntryPusher.Columns.COLUMN_TABLE_ID, Short.toString(fm.getTableId().getValue()));
+			}
+			/* should have a list of instructions, of which apply and write actions could have sublists of actions */
+			if (fm.getInstructions() != null) {
+				List<OFInstruction> instructions = fm.getInstructions();
+				for (OFInstruction inst : instructions) {
+					switch (inst.getType()) {
+					case GOTO_TABLE:
+						entry.put(StaticEntryPusher.intructionToColumnName(OFInstructionType.GOTO_TABLE), 
+								InstructionUtils.gotoTableToString(((OFInstructionGotoTable) inst)));
+						break;
+					case WRITE_METADATA:
+						entry.put(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_METADATA), 
+								InstructionUtils.writeMetadataToString(((OFInstructionWriteMetadata) inst)));
+						break;
+					case WRITE_ACTIONS:
+						entry.put(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_ACTIONS), 
+								InstructionUtils.writeActionsToString(((OFInstructionWriteActions) inst)));
+						break;
+					case APPLY_ACTIONS:
+						entry.put(StaticEntryPusher.intructionToColumnName(OFInstructionType.APPLY_ACTIONS), 
+								InstructionUtils.applyActionsToString(((OFInstructionApplyActions) inst)));
+						break;
+					case CLEAR_ACTIONS:
+						entry.put(StaticEntryPusher.intructionToColumnName(OFInstructionType.CLEAR_ACTIONS), 
+								InstructionUtils.clearActionsToString(((OFInstructionClearActions) inst)));
+						break;
+					case METER:
+						entry.put(StaticEntryPusher.intructionToColumnName(OFInstructionType.METER), 
+								InstructionUtils.meterToString(((OFInstructionMeter) inst)));
+						break;
+					case EXPERIMENTER:
+						entry.put(StaticEntryPusher.intructionToColumnName(OFInstructionType.EXPERIMENTER), 
+								InstructionUtils.experimenterToString(((OFInstructionExperimenter) inst)));
+						break;
+					case DEPRECATED:
+						entry.put(StaticEntryPusher.intructionToColumnName(OFInstructionType.DEPRECATED), 
+								InstructionUtils.deprecatedToString(((OFInstruction) inst)));
+						break;
+					case STAT_TRIGGER:
+						entry.put(StaticEntryPusher.intructionToColumnName(OFInstructionType.STAT_TRIGGER), 
+								InstructionUtils.statTriggerToJsonString(((OFInstructionStatTrigger) inst)));
+						break;
+					}
+				}
+			}	
+		}		
+
+		Match match = fm.getMatch();
+		Iterator<MatchField<?>> itr = match.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded)
+		while (itr.hasNext()) {
+			MatchField<?> mf = itr.next();
+			
+			String column = StaticEntryPusher.matchFieldToColumnName(mf.id);
+			if (match.supports(mf) && match.isExact(mf)) {
+				entry.put(column, match.get(mf).toString());
+			} else if (match.supportsMasked(mf) && match.isPartiallyMasked(mf)) {
+				entry.put(column, match.getMasked(mf).toString());
+			} else {
+				log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
+						column, match.getVersion().toString());
+			}
+		}
+				
+		int result = StaticEntryPusherResource.checkActions(entry);
+		if (result == -1)
+			throw new Exception("Invalid action/instructions");
+		
+		return entry;
+	}
+
+	/**
+	 * Turns a JSON formatted Static Flow Pusher string into a storage entry
+	 * Expects a string in JSON along the lines of:
+	 *        {
+	 *            "switch":       "AA:BB:CC:DD:EE:FF:00:11",
+	 *            "name":         "flow-mod-1",
+	 *            "cookie":       "0",
+	 *            "priority":     "32768",
+	 *            "in_port":	  "1",
+	 *            "actions":      "output=2",
+	 *        }
+	 * @param fmJson The JSON formatted static flow pusher entry
+	 * @return The map of the storage entry
+	 * @throws IOException If there was an error parsing the JSON
+	 */
+	public static Map<String, Object> jsonToStorageEntry(String fmJson) throws IOException {
+		Map<String, Object> entry = new HashMap<String, Object>();
+		MappingJsonFactory f = new MappingJsonFactory();
+		JsonParser jp;
+		
+		String tpSrcPort = null;
+		String tpDstPort = null;
+		String ipProto = null;
+
+		try {
+			jp = f.createParser(fmJson);
+		} catch (JsonParseException e) {
+			throw new IOException(e);
+		}
+
+		jp.nextToken();
+		if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+			throw new IOException("Expected START_OBJECT");
+		}
+
+		while (jp.nextToken() != JsonToken.END_OBJECT) {
+			if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
+				throw new IOException("Expected FIELD_NAME");
+			}
+
+			String n = jp.getCurrentName().toLowerCase().trim();
+			jp.nextToken();
+
+			if (n.equals(StaticEntryPusher.Columns.COLUMN_GROUP_BUCKETS)) {
+				entry.put(n, jp.readValueAsTree().toString()); /* Special case to save the entire JSON bucket tree */
+			} else if (n.equals(StaticEntryPusher.Columns.COLUMN_TP_SRC)) {
+				entry.put(n, jp.getText()); /* Support for OF1.0 generic transport ports */
+				tpSrcPort = jp.getText();
+			} else if (n.equals(StaticEntryPusher.Columns.COLUMN_TP_DST)) {
+				entry.put(n, jp.getText()); /* Support for OF1.0 generic transport ports */
+				tpDstPort = jp.getText();
+			} else if (n.equals(StaticEntryPusher.matchFieldToColumnName(MatchFields.IP_PROTO))) {
+				entry.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.IP_PROTO), jp.getText());
+				ipProto = jp.getText(); /* Support for OF1.0 generic transport ports */
+			} else { 
+				entry.put(n, jp.getText()); /* All others are 'key':'value' pairs */
+			}
+		} 
+		
+		// For OF1.0, transport ports are specified using generic tp_src, tp_dst type strings.
+		// Once the whole json string has been parsed, find out the IpProto to properly assign the ports.
+		// If IpProto not specified, print error, and make sure all TP columns are clear.
+		if (ipProto != null && ipProto.equalsIgnoreCase("tcp")) {
+			if (tpSrcPort != null) {
+				entry.remove(StaticEntryPusher.Columns.COLUMN_TP_SRC);
+				entry.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.TCP_SRC), tpSrcPort);
+			}
+			if (tpDstPort != null) {
+				entry.remove(StaticEntryPusher.Columns.COLUMN_TP_DST);
+				entry.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.TCP_DST), tpDstPort);
+			}
+		} else if (ipProto != null && ipProto.equalsIgnoreCase("udp")) {
+			if (tpSrcPort != null) {
+				entry.remove(StaticEntryPusher.Columns.COLUMN_TP_SRC);
+				entry.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.UDP_SRC), tpSrcPort);
+			}
+			if (tpDstPort != null) {
+				entry.remove(StaticEntryPusher.Columns.COLUMN_TP_DST);
+				entry.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.UDP_DST), tpDstPort);
+			}
+		} else if (ipProto != null && ipProto.equalsIgnoreCase("sctp")) {
+			if (tpSrcPort != null) {
+				entry.remove(StaticEntryPusher.Columns.COLUMN_TP_SRC);
+				entry.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.SCTP_SRC), tpSrcPort);
+			}
+			if (tpDstPort != null) {
+				entry.remove(StaticEntryPusher.Columns.COLUMN_TP_DST);
+				entry.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.SCTP_DST), tpDstPort);
+			}
+		} else {
+			log.debug("Got IP protocol of '{}' and tp-src of '{}' and tp-dst of '" + tpDstPort + "' via SFP REST API", ipProto, tpSrcPort);
+		}
+
+		return entry;
+	}   
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticentry/StaticEntryPusher.java
similarity index 52%
rename from src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
rename to src/main/java/net/floodlightcontroller/staticentry/StaticEntryPusher.java
index c5f50b55e43081107abdbab98a97ac99f9bf9017..f45c241c92f96bfe5a9c4edf52009dbc58c666cf 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
+++ b/src/main/java/net/floodlightcontroller/staticentry/StaticEntryPusher.java
@@ -14,19 +14,21 @@
  *    under the License.
  **/
 
-package net.floodlightcontroller.staticflowentry;
+package net.floodlightcontroller.staticentry;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
 
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.HAListenerTypeMarker;
@@ -43,155 +45,90 @@ import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.core.util.AppCookie;
 import net.floodlightcontroller.restserver.IRestApiService;
-import net.floodlightcontroller.staticflowentry.web.StaticFlowEntryWebRoutable;
+import net.floodlightcontroller.staticentry.web.StaticEntryWebRoutable;
+import net.floodlightcontroller.staticentry.web.StaticFlowEntryWebRoutable;
+import net.floodlightcontroller.staticentry.web.StaticFlowWebRoutable;
 import net.floodlightcontroller.storage.IResultSet;
 import net.floodlightcontroller.storage.IStorageSourceListener;
 import net.floodlightcontroller.storage.IStorageSourceService;
 import net.floodlightcontroller.storage.StorageException;
 import net.floodlightcontroller.util.ActionUtils;
 import net.floodlightcontroller.util.FlowModUtils;
+import net.floodlightcontroller.util.GroupUtils;
 import net.floodlightcontroller.util.InstructionUtils;
 import net.floodlightcontroller.util.MatchUtils;
 
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFlowAdd;
-import org.projectfloodlight.openflow.protocol.OFFlowDeleteStrict;
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
 import org.projectfloodlight.openflow.protocol.OFFlowRemovedReason;
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+import org.projectfloodlight.openflow.protocol.OFInstructionType;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFType;
 import org.projectfloodlight.openflow.protocol.OFVersion;
-import org.projectfloodlight.openflow.protocol.ver10.OFFlowRemovedReasonSerializerVer10;
-import org.projectfloodlight.openflow.protocol.ver11.OFFlowRemovedReasonSerializerVer11;
-import org.projectfloodlight.openflow.protocol.ver12.OFFlowRemovedReasonSerializerVer12;
-import org.projectfloodlight.openflow.protocol.ver13.OFFlowRemovedReasonSerializerVer13;
-import org.projectfloodlight.openflow.protocol.ver14.OFFlowRemovedReasonSerializerVer14;
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.TableId;
-import org.projectfloodlight.openflow.types.U16;
+import org.projectfloodlight.openflow.types.U32;
 import org.projectfloodlight.openflow.types.U64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableSet;
+
 /**
  * This module is responsible for maintaining a set of static flows on
- * switches. This is just a big 'ol dumb list of flows and something external
+ * switches. This is just a big 'ol dumb list of flows and groups and something external
  * is responsible for ensuring they make sense for the network.
  */
-public class StaticFlowEntryPusher
-implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, IStorageSourceListener, IOFMessageListener {
-	protected static Logger log = LoggerFactory.getLogger(StaticFlowEntryPusher.class);
-	public static final String StaticFlowName = "staticflowentry";
+public class StaticEntryPusher
+implements IOFSwitchListener, IFloodlightModule, IStaticEntryPusherService, IStorageSourceListener, IOFMessageListener {
+	protected static Logger log = LoggerFactory.getLogger(StaticEntryPusher.class);
+	public static final String MODULE_NAME = "staticentrypusher";
 
-	public static final int STATIC_FLOW_APP_ID = 10;
+	public static final int STATIC_ENTRY_APP_ID = 10;
 	static {
-		AppCookie.registerApp(STATIC_FLOW_APP_ID, StaticFlowName);
-	}
-
-	public static final String TABLE_NAME = "controller_staticflowtableentry";
-	public static final String COLUMN_NAME = "name";
-	public static final String COLUMN_SWITCH = "switch";
-	public static final String COLUMN_TABLE_ID = "table";
-	public static final String COLUMN_ACTIVE = "active";
-	public static final String COLUMN_IDLE_TIMEOUT = "idle_timeout";
-	public static final String COLUMN_HARD_TIMEOUT = "hard_timeout";
-	public static final String COLUMN_PRIORITY = "priority";
-	public static final String COLUMN_COOKIE = "cookie";
-
-	// Common location for Match Strings. Still the same, but relocated.
-	public static final String COLUMN_IN_PORT = MatchUtils.STR_IN_PORT;
-
-	public static final String COLUMN_DL_SRC = MatchUtils.STR_DL_SRC;
-	public static final String COLUMN_DL_DST = MatchUtils.STR_DL_DST;
-	public static final String COLUMN_DL_VLAN = MatchUtils.STR_DL_VLAN;
-	public static final String COLUMN_DL_VLAN_PCP = MatchUtils.STR_DL_VLAN_PCP;
-	public static final String COLUMN_DL_TYPE = MatchUtils.STR_DL_TYPE;
-
-	public static final String COLUMN_NW_TOS = MatchUtils.STR_NW_TOS;
-	public static final String COLUMN_NW_ECN = MatchUtils.STR_NW_ECN;
-	public static final String COLUMN_NW_DSCP = MatchUtils.STR_NW_DSCP;
-	public static final String COLUMN_NW_PROTO = MatchUtils.STR_NW_PROTO;
-	public static final String COLUMN_NW_SRC = MatchUtils.STR_NW_SRC; // includes CIDR-style netmask, e.g. "128.8.128.0/24"
-	public static final String COLUMN_NW_DST = MatchUtils.STR_NW_DST;
-
-	public static final String COLUMN_SCTP_SRC = MatchUtils.STR_SCTP_SRC;
-	public static final String COLUMN_SCTP_DST = MatchUtils.STR_SCTP_DST;
-	public static final String COLUMN_UDP_SRC = MatchUtils.STR_UDP_SRC;
-	public static final String COLUMN_UDP_DST = MatchUtils.STR_UDP_DST;
-	public static final String COLUMN_TCP_SRC = MatchUtils.STR_TCP_SRC;
-	public static final String COLUMN_TCP_DST = MatchUtils.STR_TCP_DST;
-	public static final String COLUMN_TP_SRC = MatchUtils.STR_TP_SRC; // support for OF1.0 generic transport ports (possibly sent from the rest api). Only use these to read them in, but store them as the type of port their IpProto is set to.
-	public static final String COLUMN_TP_DST = MatchUtils.STR_TP_DST;
-
-	/* newly added matches for OF1.3 port start here */
-	public static final String COLUMN_ICMP_TYPE = MatchUtils.STR_ICMP_TYPE;
-	public static final String COLUMN_ICMP_CODE = MatchUtils.STR_ICMP_CODE;
-
-	public static final String COLUMN_ARP_OPCODE = MatchUtils.STR_ARP_OPCODE;
-	public static final String COLUMN_ARP_SHA = MatchUtils.STR_ARP_SHA;
-	public static final String COLUMN_ARP_DHA = MatchUtils.STR_ARP_DHA;
-	public static final String COLUMN_ARP_SPA = MatchUtils.STR_ARP_SPA;
-	public static final String COLUMN_ARP_DPA = MatchUtils.STR_ARP_DPA;
-
-	/* IPv6 related columns */
-	public static final String COLUMN_NW6_SRC = MatchUtils.STR_IPV6_SRC;
-	public static final String COLUMN_NW6_DST = MatchUtils.STR_IPV6_DST;
-	public static final String COLUMN_IPV6_FLOW_LABEL = MatchUtils.STR_IPV6_FLOW_LABEL;
-	public static final String COLUMN_ICMP6_TYPE = MatchUtils.STR_ICMPV6_TYPE;
-	public static final String COLUMN_ICMP6_CODE = MatchUtils.STR_ICMPV6_CODE;
-	public static final String COLUMN_ND_SLL = MatchUtils.STR_IPV6_ND_SSL;
-	public static final String COLUMN_ND_TLL = MatchUtils.STR_IPV6_ND_TTL;
-	public static final String COLUMN_ND_TARGET = MatchUtils.STR_IPV6_ND_TARGET;	
-
-	public static final String COLUMN_MPLS_LABEL = MatchUtils.STR_MPLS_LABEL;
-	public static final String COLUMN_MPLS_TC = MatchUtils.STR_MPLS_TC;
-	public static final String COLUMN_MPLS_BOS = MatchUtils.STR_MPLS_BOS;
-
-	public static final String COLUMN_METADATA = MatchUtils.STR_METADATA;
-	public static final String COLUMN_TUNNEL_ID = MatchUtils.STR_TUNNEL_ID;
-
-	public static final String COLUMN_PBB_ISID = MatchUtils.STR_PBB_ISID;
-	/* end newly added matches */
-
-	public static final String COLUMN_ACTIONS = "actions";
-
-	public static final String COLUMN_INSTR_GOTO_TABLE = InstructionUtils.STR_GOTO_TABLE; // instructions are each getting their own column, due to write and apply actions, which themselves contain a variable list of actions
-	public static final String COLUMN_INSTR_WRITE_METADATA = InstructionUtils.STR_WRITE_METADATA;
-	public static final String COLUMN_INSTR_WRITE_ACTIONS = InstructionUtils.STR_WRITE_ACTIONS;
-	public static final String COLUMN_INSTR_APPLY_ACTIONS = InstructionUtils.STR_APPLY_ACTIONS;
-	public static final String COLUMN_INSTR_CLEAR_ACTIONS = InstructionUtils.STR_CLEAR_ACTIONS;
-	public static final String COLUMN_INSTR_GOTO_METER = InstructionUtils.STR_GOTO_METER;
-	public static final String COLUMN_INSTR_EXPERIMENTER = InstructionUtils.STR_EXPERIMENTER;
-
-	public static String ColumnNames[] = { COLUMN_NAME, COLUMN_SWITCH,
-		COLUMN_TABLE_ID, COLUMN_ACTIVE, COLUMN_IDLE_TIMEOUT, COLUMN_HARD_TIMEOUT, // table id is new for OF1.3 as well
-		COLUMN_PRIORITY, COLUMN_COOKIE, COLUMN_IN_PORT,
-		COLUMN_DL_SRC, COLUMN_DL_DST, COLUMN_DL_VLAN, COLUMN_DL_VLAN_PCP,
-		COLUMN_DL_TYPE, COLUMN_NW_TOS, COLUMN_NW_PROTO, COLUMN_NW_SRC,
-		COLUMN_NW_DST, COLUMN_TP_SRC, COLUMN_TP_DST,
-		/* newly added matches for OF1.3 port start here */
-		COLUMN_SCTP_SRC, COLUMN_SCTP_DST, 
-		COLUMN_UDP_SRC, COLUMN_UDP_DST, COLUMN_TCP_SRC, COLUMN_TCP_DST,
-		COLUMN_ICMP_TYPE, COLUMN_ICMP_CODE, 
-		COLUMN_ARP_OPCODE, COLUMN_ARP_SHA, COLUMN_ARP_DHA, 
-		COLUMN_ARP_SPA, COLUMN_ARP_DPA,
-
-		/* IPv6 related matches */
-		COLUMN_NW6_SRC, COLUMN_NW6_DST, COLUMN_ICMP6_TYPE, COLUMN_ICMP6_CODE, 
-		COLUMN_IPV6_FLOW_LABEL, COLUMN_ND_SLL, COLUMN_ND_TLL, COLUMN_ND_TARGET,		
-		COLUMN_MPLS_LABEL, COLUMN_MPLS_TC, COLUMN_MPLS_BOS, 
-		COLUMN_METADATA, COLUMN_TUNNEL_ID, COLUMN_PBB_ISID,
-		/* end newly added matches */
-		COLUMN_ACTIONS,
-		/* newly added instructions for OF1.3 port start here */
-		COLUMN_INSTR_GOTO_TABLE, COLUMN_INSTR_WRITE_METADATA,
-		COLUMN_INSTR_WRITE_ACTIONS, COLUMN_INSTR_APPLY_ACTIONS,
-		COLUMN_INSTR_CLEAR_ACTIONS, COLUMN_INSTR_GOTO_METER,
-		COLUMN_INSTR_EXPERIMENTER
-		/* end newly added instructions */
-	};
+		AppCookie.registerApp(STATIC_ENTRY_APP_ID, MODULE_NAME);
+	}
+
+	public static final String TABLE_NAME = "controller_staticentrytable";
+
+	public static class Columns {
+		public static final String COLUMN_NAME = "name";
+		public static final String COLUMN_ENTRY_TYPE = "entry_type";
+		public static final String ENTRY_TYPE_FLOW = "flow";
+		public static final String ENTRY_TYPE_GROUP = "group";
+		public static final String COLUMN_SWITCH = "switch";
+		public static final String COLUMN_TABLE_ID = "table";
+		public static final String COLUMN_ACTIVE = "active";
+		public static final String COLUMN_IDLE_TIMEOUT = "idle_timeout";
+		public static final String COLUMN_HARD_TIMEOUT = "hard_timeout";
+		public static final String COLUMN_PRIORITY = "priority";
+		public static final String COLUMN_COOKIE = "cookie";
+
+		/* NOTE: Use MatchUtil's names for MatchField column names */
+
+		/* 
+		 * Support for OF1.0 generic transport ports (possibly from the REST API). 
+		 * Only use these to read them in, but store them as the type of port their IpProto
+		 */
+		public static final String COLUMN_NW_TOS = MatchUtils.STR_NW_TOS;
+		public static final String COLUMN_TP_SRC = MatchUtils.STR_TP_SRC;
+		public static final String COLUMN_TP_DST = MatchUtils.STR_TP_DST;
+
+		public static final String COLUMN_ACTIONS = "actions";
+
+		/* NOTE: Use InstructionUtil's names for Instruction column names */
+
+		public static final String COLUMN_GROUP_TYPE = GroupUtils.GROUP_TYPE;
+		public static final String COLUMN_GROUP_BUCKETS = GroupUtils.GROUP_BUCKETS;
+		public static final String COLUMN_GROUP_ID = GroupUtils.GROUP_ID;
+
+		private static Set<String> ALL_COLUMNS;	/* Use internally to query only */
+	}
 
 	protected IFloodlightProviderService floodlightProviderService;
 	protected IOFSwitchService switchService;
@@ -200,8 +137,8 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 
 	private IHAListener haListener;
 
-	// Map<DPID, Map<Name, FlowMod>>; FlowMod can be null to indicate non-active
-	protected Map<String, Map<String, OFFlowMod>> entriesFromStorage;
+	// Map<DPID, Map<Name, OFMessage>>; OFMessage can be null to indicate non-active
+	protected Map<String, Map<String, OFMessage>> entriesFromStorage;
 	// Entry Name -> DPID of Switch it's on
 	protected Map<String, String> entry2dpid;
 
@@ -213,14 +150,31 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		}
 		@Override
 		public int compare(String o1, String o2) {
-			OFFlowMod f1 = entriesFromStorage.get(dpid).get(o1);
-			OFFlowMod f2 = entriesFromStorage.get(dpid).get(o2);
-			if (f1 == null || f2 == null) // sort active=false flows by key
+			OFMessage m1 = entriesFromStorage.get(dpid).get(o1);
+			OFMessage m2 = entriesFromStorage.get(dpid).get(o2);
+			if (m1 == null || m2 == null) {// sort active=false flows by key
 				return o1.compareTo(o2);
-			return U16.of(f1.getPriority()).getValue() - U16.of(f2.getPriority()).getValue();
+			}
+			if (m1 instanceof OFFlowMod && m2 instanceof OFFlowMod) {
+				return (int) (U32.of(((OFFlowMod) m1).getPriority()).getValue() - U32.of(((OFFlowMod) m2).getPriority()).getValue());
+			} else if (m1 instanceof OFFlowMod) {
+				return 1;
+			} else if (m2 instanceof OFFlowMod) {
+				return -1;
+			} else {
+				return 0;
+			}
 		}
 	};
 
+	public static String matchFieldToColumnName(MatchFields mf) {
+		return MatchUtils.getMatchFieldName(mf);
+	}
+	
+	public static String intructionToColumnName(OFInstructionType t) {
+		return InstructionUtils.getInstructionName(t);
+	}
+	
 	/**
 	 * used for debugging and unittests
 	 * @return the number of static flow entries as cached from storage
@@ -268,17 +222,17 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		String stringId = sw.getId().toString();
 
 		if ((entriesFromStorage != null) && (entriesFromStorage.containsKey(stringId))) {
-			Map<String, OFFlowMod> entries = entriesFromStorage.get(stringId);
+			Map<String, OFMessage> entries = entriesFromStorage.get(stringId);
 			List<String> sortedList = new ArrayList<String>(entries.keySet());
 			// weird that Collections.sort() returns void
 			Collections.sort( sortedList, new FlowModSorter(stringId));
 			for (String entryName : sortedList) {
-				OFFlowMod flowMod = entries.get(entryName);
-				if (flowMod != null) {
+				OFMessage message = entries.get(entryName);
+				if (message != null) {
 					if (log.isDebugEnabled()) {
 						log.debug("Pushing static entry {} for {}", stringId, entryName);
 					}
-					writeFlowModToSwitch(sw, flowMod);
+					writeOFMessageToSwitch(sw.getId(), message);
 				}
 			}
 		}
@@ -292,7 +246,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	 */
 
 	protected Map<String, String> computeEntry2DpidMap(
-			Map<String, Map<String, OFFlowMod>> map) {
+			Map<String, Map<String, OFMessage>> map) {
 		Map<String, String> ret = new ConcurrentHashMap<String, String>();
 		for(String dpid : map.keySet()) {
 			for( String entry: map.get(dpid).keySet())
@@ -306,12 +260,12 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	 *
 	 * @return
 	 */
-	private Map<String, Map<String, OFFlowMod>> readEntriesFromStorage() {
-		Map<String, Map<String, OFFlowMod>> entries = new ConcurrentHashMap<String, Map<String, OFFlowMod>>();
+	private Map<String, Map<String, OFMessage>> readEntriesFromStorage() {
+		Map<String, Map<String, OFMessage>> entries = new ConcurrentHashMap<String, Map<String, OFMessage>>();
 		try {
 			Map<String, Object> row;
 			// null1=no predicate, null2=no ordering
-			IResultSet resultSet = storageSourceService.executeQuery(TABLE_NAME, ColumnNames, null, null);
+			IResultSet resultSet = storageSourceService.executeQuery(TABLE_NAME, Columns.ALL_COLUMNS.toArray(new String[Columns.ALL_COLUMNS.size()]), null, null);
 			for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) {
 				row = it.next().getRow();
 				parseRow(row, entries);
@@ -325,37 +279,53 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	}
 
 	/**
-	 * Take a single row, turn it into a flowMod, and add it to the
-	 * entries{$dpid}.{$entryName}=FlowMod
-	 *
-	 * IF an entry is in active, mark it with FlowMod = null
+	 * Take a single row, turn it into a entry.
+	 * If an entry is inactive, mark it with null
 	 *
 	 * @param row
 	 * @param entries
 	 */
-	void parseRow(Map<String, Object> row, Map<String, Map<String, OFFlowMod>> entries) {
+	void parseRow(Map<String, Object> row, Map<String, Map<String, OFMessage>> entries) {
 		String switchName = null;
 		String entryName = null;
+		String entryType = Columns.ENTRY_TYPE_FLOW;
 
 		StringBuffer matchString = new StringBuffer();
 		OFFlowMod.Builder fmb = null; 
+		OFGroupMod.Builder gmb = null;
 
-		if (!row.containsKey(COLUMN_SWITCH) || !row.containsKey(COLUMN_NAME)) {
-			log.debug("skipping entry with missing required 'switch' or 'name' entry: {}", row);
+		if (!row.containsKey(Columns.COLUMN_SWITCH) || !row.containsKey(Columns.COLUMN_NAME)) {
+			log.warn("Skipping entry with missing required 'switch' or 'name' entry: {}", row);
 			return;
 		}
-		// most error checking done with ClassCastException
+		
 		try {
-			// first, snag the required entries, for debugging info
-			switchName = (String) row.get(COLUMN_SWITCH);
-			entryName = (String) row.get(COLUMN_NAME);
+			switchName = (String) row.get(Columns.COLUMN_SWITCH);
+			entryName = (String) row.get(Columns.COLUMN_NAME);
+
+			String tmp = (String) row.get(Columns.COLUMN_ENTRY_TYPE);
+			if (tmp != null) {
+				tmp = tmp.toLowerCase().trim();
+				if (tmp.equals(Columns.ENTRY_TYPE_GROUP)) {
+					entryType = Columns.ENTRY_TYPE_GROUP;
+				}
+			} /* else use default of flow */
+
 			if (!entries.containsKey(switchName)) {
-				entries.put(switchName, new HashMap<String, OFFlowMod>());
+				entries.put(switchName, new HashMap<String, OFMessage>());
 			}
 
-			// get the correct builder for the OF version supported by the switch
+			/* get the correct builder for the OF version supported by the switch */
 			try {
-				fmb = OFFactories.getFactory(switchService.getSwitch(DatapathId.of(switchName)).getOFFactory().getVersion()).buildFlowModify();
+				if (entryType.equals(Columns.ENTRY_TYPE_FLOW)) {
+					fmb = OFFactories.getFactory(switchService.getSwitch(DatapathId.of(switchName)).getOFFactory().getVersion()).buildFlowModify();
+					StaticEntries.initDefaultFlowMod(fmb, entryName);
+				} else if (entryType.equals(Columns.ENTRY_TYPE_GROUP)) {
+					gmb = OFFactories.getFactory(switchService.getSwitch(DatapathId.of(switchName)).getOFFactory().getVersion()).buildGroupModify();
+				} else {
+					log.error("Not adding a flow or a group? Possible Static Flow Pusher bug");
+					return;
+				}
 			} catch (NullPointerException e) {
 				/* switch was not connected/known */
 				storageSourceService.deleteRowAsync(TABLE_NAME, entryName);
@@ -363,54 +333,66 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 				return;
 			}
 
-			StaticFlowEntries.initDefaultFlowMod(fmb, entryName);
-
 			for (String key : row.keySet()) {
 				if (row.get(key) == null) {
 					continue;
 				}
 
-				if (key.equals(COLUMN_SWITCH) || key.equals(COLUMN_NAME) || key.equals("id")) {
+				if (key.equals(Columns.COLUMN_SWITCH) || key.equals(Columns.COLUMN_NAME)) {
 					continue; // already handled
 				}
 
-				if (key.equals(COLUMN_ACTIVE)) {
-					if  (!Boolean.valueOf((String) row.get(COLUMN_ACTIVE))) {
+				if (key.equals(Columns.COLUMN_ACTIVE)) {
+					if (!Boolean.valueOf((String) row.get(Columns.COLUMN_ACTIVE))) {
 						log.debug("skipping inactive entry {} for switch {}", entryName, switchName);
 						entries.get(switchName).put(entryName, null);  // mark this an inactive
 						return;
 					}
-				} else if (key.equals(COLUMN_HARD_TIMEOUT)) {
-					fmb.setHardTimeout(Integer.valueOf((String) row.get(COLUMN_HARD_TIMEOUT)));
-				} else if (key.equals(COLUMN_IDLE_TIMEOUT)) {
-					fmb.setIdleTimeout(Integer.valueOf((String) row.get(COLUMN_IDLE_TIMEOUT)));
-				} else if (key.equals(COLUMN_TABLE_ID)) {
+				} else if (key.equals(Columns.COLUMN_HARD_TIMEOUT) && fmb != null) {
+					fmb.setHardTimeout(Integer.valueOf((String) row.get(Columns.COLUMN_HARD_TIMEOUT)));
+				} else if (key.equals(Columns.COLUMN_IDLE_TIMEOUT) && fmb != null) {
+					fmb.setIdleTimeout(Integer.valueOf((String) row.get(Columns.COLUMN_IDLE_TIMEOUT)));
+				} else if (key.equals(Columns.COLUMN_TABLE_ID) && fmb != null) {
 					if (fmb.getVersion().compareTo(OFVersion.OF_10) > 0) {
 						fmb.setTableId(TableId.of(Integer.parseInt((String) row.get(key)))); // support multiple flow tables for OF1.1+
 					} else {
 						log.error("Table not supported in OpenFlow 1.0");
 					}
-				} else if (key.equals(COLUMN_ACTIONS)) {
-					ActionUtils.fromString(fmb, (String) row.get(COLUMN_ACTIONS), log);
-				} else if (key.equals(COLUMN_COOKIE)) {
-					fmb.setCookie(StaticFlowEntries.computeEntryCookie(Integer.valueOf((String) row.get(COLUMN_COOKIE)), entryName));
-				} else if (key.equals(COLUMN_PRIORITY)) {
-					fmb.setPriority(U16.t(Integer.valueOf((String) row.get(COLUMN_PRIORITY))));
-				} else if (key.equals(COLUMN_INSTR_APPLY_ACTIONS)) {
-					InstructionUtils.applyActionsFromString(fmb, (String) row.get(COLUMN_INSTR_APPLY_ACTIONS), log);
-				} else if (key.equals(COLUMN_INSTR_CLEAR_ACTIONS)) {
-					InstructionUtils.clearActionsFromString(fmb, (String) row.get(COLUMN_INSTR_CLEAR_ACTIONS), log);
-				} else if (key.equals(COLUMN_INSTR_EXPERIMENTER)) {
-					InstructionUtils.experimenterFromString(fmb, (String) row.get(COLUMN_INSTR_EXPERIMENTER), log);
-				} else if (key.equals(COLUMN_INSTR_GOTO_METER)) {
-					InstructionUtils.meterFromString(fmb, (String) row.get(COLUMN_INSTR_GOTO_METER), log);
-				} else if (key.equals(COLUMN_INSTR_GOTO_TABLE)) {
-					InstructionUtils.gotoTableFromString(fmb, (String) row.get(COLUMN_INSTR_GOTO_TABLE), log);
-				} else if (key.equals(COLUMN_INSTR_WRITE_ACTIONS)) {
-					InstructionUtils.writeActionsFromString(fmb, (String) row.get(COLUMN_INSTR_WRITE_ACTIONS), log);
-				} else if (key.equals(COLUMN_INSTR_WRITE_METADATA)) {
-					InstructionUtils.writeMetadataFromString(fmb, (String) row.get(COLUMN_INSTR_WRITE_METADATA), log);
-				} else { // the rest of the keys are for Match().fromString()
+				} else if (key.equals(Columns.COLUMN_ACTIONS) && fmb != null) {
+					ActionUtils.fromString(fmb, (String) row.get(Columns.COLUMN_ACTIONS));
+				} else if (key.equals(Columns.COLUMN_COOKIE) && fmb != null) {
+					fmb.setCookie(StaticEntries.computeEntryCookie(Integer.valueOf((String) row.get(Columns.COLUMN_COOKIE)), entryName));
+				} else if (key.equals(Columns.COLUMN_PRIORITY) && fmb != null) {
+					fmb.setPriority(U32.t(Integer.valueOf((String) row.get(Columns.COLUMN_PRIORITY))));
+				} else if (key.equals(StaticEntryPusher.intructionToColumnName(OFInstructionType.APPLY_ACTIONS)) && fmb != null) {
+					InstructionUtils.applyActionsFromString(fmb, 
+							(String) row.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.APPLY_ACTIONS)));
+				} else if (key.equals(StaticEntryPusher.intructionToColumnName(OFInstructionType.CLEAR_ACTIONS)) && fmb != null) {
+					InstructionUtils.clearActionsFromString(fmb, 
+							(String) row.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.CLEAR_ACTIONS)));
+				} else if (key.equals(StaticEntryPusher.intructionToColumnName(OFInstructionType.EXPERIMENTER)) && fmb != null) {
+					InstructionUtils.experimenterFromString(fmb, 
+							(String) row.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.EXPERIMENTER)));
+				} else if (key.equals(StaticEntryPusher.intructionToColumnName(OFInstructionType.METER)) && fmb != null) {
+					InstructionUtils.meterFromString(fmb, 
+							(String) row.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.METER)));
+				} else if (key.equals(StaticEntryPusher.intructionToColumnName(OFInstructionType.GOTO_TABLE)) && fmb != null) {
+					InstructionUtils.gotoTableFromString(fmb, 
+							(String) row.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.GOTO_TABLE)));
+				} else if (key.equals(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_ACTIONS)) && fmb != null) {
+					InstructionUtils.writeActionsFromString(fmb, 
+							(String) row.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_ACTIONS)));
+				} else if (key.equals(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_METADATA)) && fmb != null) {
+					InstructionUtils.writeMetadataFromString(fmb, 
+							(String) row.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_METADATA)));
+				} else if (key.equals(Columns.COLUMN_GROUP_TYPE) && gmb != null) {
+					GroupUtils.setGroupTypeFromString(gmb, (String) row.get(Columns.COLUMN_GROUP_TYPE));
+				} else if (key.equals(Columns.COLUMN_GROUP_BUCKETS) && gmb != null) {
+					GroupUtils.setGroupBucketsFromJsonArray(gmb, (String) row.get(Columns.COLUMN_GROUP_BUCKETS));
+				} else if (key.equals(Columns.COLUMN_GROUP_ID) && gmb != null) {
+					GroupUtils.setGroupIdFromString(gmb, (String) row.get(Columns.COLUMN_GROUP_ID));
+				} else if (fmb != null) { // the rest of the keys are for Match().fromString()
+
 					if (matchString.length() > 0) {
 						matchString.append(",");
 					}
@@ -425,21 +407,26 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 			}
 		}
 
-		String match = matchString.toString();
+		if (fmb != null) {
+			String match = matchString.toString();
 
-		try {
-			fmb.setMatch(MatchUtils.fromString(match, fmb.getVersion()));
-		} catch (IllegalArgumentException e) {
-			log.error(e.toString());
-			log.error("Ignoring flow entry {} on switch {} with illegal OFMatch() key: " + match, entryName, switchName);
-			return;
-		} catch (Exception e) {
-			log.error("OF version incompatible for the match: " + match);
-			e.printStackTrace();
-			return;
+			try {
+				fmb.setMatch(MatchUtils.fromString(match, fmb.getVersion()));
+			} catch (IllegalArgumentException e) {
+				log.error(e.toString());
+				log.error("Ignoring flow entry {} on switch {} with illegal OFMatch() key: " + match, entryName, switchName);
+				return;
+			} catch (Exception e) {
+				log.error("OF version incompatible for the match: " + match);
+				e.printStackTrace();
+				return;
+			}
+			entries.get(switchName).put(entryName, fmb.build());
+		} else if (gmb != null) {
+			entries.get(switchName).put(entryName, gmb.build());
+		} else {
+			log.error("Processed neither flow nor group mod. Possible Static Flow Pusher bug");
 		}
-
-		entries.get(switchName).put(entryName, fmb.build()); // add the FlowMod message to the table
 	}
 
 	@Override
@@ -456,29 +443,21 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	}
 
 	@Override
-	public void switchActivated(DatapathId switchId) {
-		// no-op
-	}
+	public void switchActivated(DatapathId switchId) {}
 
 	@Override
-	public void switchChanged(DatapathId switchId) {
-		// no-op
-	}
+	public void switchChanged(DatapathId switchId) {}
 
 	@Override
-	public void switchPortChanged(DatapathId switchId,
-			OFPortDesc port,
-			PortChangeType type) {
-		// no-op
-	}
+	public void switchPortChanged(DatapathId switchId, OFPortDesc port, PortChangeType type) {}
 
 
 	@Override
 	public void rowsModified(String tableName, Set<Object> rowKeys) {
 		// This handles both rowInsert() and rowUpdate()
 		log.debug("Modifying Table {}", tableName);
-		HashMap<String, Map<String, OFFlowMod>> entriesToAdd =
-				new HashMap<String, Map<String, OFFlowMod>>();
+		HashMap<String, Map<String, OFMessage>> entriesToAdd =
+				new HashMap<String, Map<String, OFMessage>>();
 		// build up list of what was added
 		for (Object key: rowKeys) {
 			IResultSet resultSet = storageSourceService.getRow(tableName, key);
@@ -491,60 +470,113 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		// batch updates by switch and blast them out
 		for (String dpid : entriesToAdd.keySet()) {
 			if (!entriesFromStorage.containsKey(dpid))
-				entriesFromStorage.put(dpid, new HashMap<String, OFFlowMod>());
+				entriesFromStorage.put(dpid, new HashMap<String, OFMessage>());
 
 			List<OFMessage> outQueue = new ArrayList<OFMessage>();
 
 			/* For every flow per dpid, decide how to "add" the flow. */
 			for (String entry : entriesToAdd.get(dpid).keySet()) {
-				OFFlowMod newFlowMod = entriesToAdd.get(dpid).get(entry);
+				OFFlowMod newFlowMod = null;
 				OFFlowMod oldFlowMod = null;
+				OFGroupMod newGroupMod = null;
+				OFGroupMod oldGroupMod = null;
 
-				String dpidOldFlowMod = entry2dpid.get(entry);
-				if (dpidOldFlowMod != null) {
-					oldFlowMod = entriesFromStorage.get(dpidOldFlowMod).remove(entry);
+				if (entriesToAdd.get(dpid).get(entry) instanceof OFFlowMod) {
+					newFlowMod = (OFFlowMod) entriesToAdd.get(dpid).get(entry);
+				} else if (entriesToAdd.get(dpid).get(entry) instanceof OFGroupMod) {
+					newGroupMod = (OFGroupMod) entriesToAdd.get(dpid).get(entry);
 				}
-
-				/* Modify, which can be either a Flow MODIFY_STRICT or a Flow DELETE_STRICT with a side of Flow ADD */
-				if (oldFlowMod != null && newFlowMod != null) { 
-					/* MODIFY_STRICT b/c the match is still the same */
-					if (oldFlowMod.getMatch().equals(newFlowMod.getMatch())
-							&& oldFlowMod.getCookie().equals(newFlowMod.getCookie())
-							&& oldFlowMod.getPriority() == newFlowMod.getPriority()
-							&& dpidOldFlowMod.equalsIgnoreCase(dpid)) {
-						log.debug("ModifyStrict SFP Flow");
-						entriesFromStorage.get(dpid).put(entry, newFlowMod);
-						entry2dpid.put(entry, dpid);
-						newFlowMod = FlowModUtils.toFlowModifyStrict(newFlowMod);
-						outQueue.add(newFlowMod);
-						/* DELETE_STRICT and then ADD b/c the match is now different */
-					} else {
-						log.debug("DeleteStrict and Add SFP Flow");
-						oldFlowMod = FlowModUtils.toFlowDeleteStrict(oldFlowMod);
-						OFFlowAdd addTmp = FlowModUtils.toFlowAdd(newFlowMod);
-						/* If the flow's dpid and the current switch we're looking at are the same, add to the queue. */
-						if (dpidOldFlowMod.equals(dpid)) {
-							outQueue.add(oldFlowMod);
-							outQueue.add(addTmp); 
-							/* Otherwise, go ahead and send the flows now (since queuing them will send to the wrong switch). */
+				final boolean isFlowMod = newFlowMod == null ? false : true;
+
+
+				String oldDpid = entry2dpid.get(entry);
+
+				if (isFlowMod) {
+					if (oldDpid != null) {
+						oldFlowMod = (OFFlowMod) entriesFromStorage.get(oldDpid).remove(entry);
+					} 
+
+					/* Modify, which can be either a Flow MODIFY_STRICT or a Flow DELETE_STRICT with a side of Flow ADD */
+					if (oldFlowMod != null && newFlowMod != null) { 
+						/* MODIFY_STRICT b/c the match is still the same */
+						if (oldFlowMod.getMatch().equals(newFlowMod.getMatch())
+								&& oldFlowMod.getCookie().equals(newFlowMod.getCookie())
+								&& oldFlowMod.getPriority() == newFlowMod.getPriority()
+								&& oldDpid.equalsIgnoreCase(dpid)) {
+							log.debug("ModifyStrict SFP Flow");
+							entriesFromStorage.get(dpid).put(entry, newFlowMod);
+							entry2dpid.put(entry, dpid);
+							newFlowMod = FlowModUtils.toFlowModifyStrict(newFlowMod);
+							outQueue.add(newFlowMod);
+							/* DELETE_STRICT and then ADD b/c the match is now different */
 						} else {
-							writeOFMessageToSwitch(DatapathId.of(dpidOldFlowMod), oldFlowMod);
-							writeOFMessageToSwitch(DatapathId.of(dpid), FlowModUtils.toFlowAdd(newFlowMod)); 
+							log.debug("DeleteStrict and Add SFP Flow");
+							oldFlowMod = FlowModUtils.toFlowDeleteStrict(oldFlowMod);
+							OFFlowAdd addTmp = FlowModUtils.toFlowAdd(newFlowMod);
+							/* If the flow's dpid and the current switch we're looking at are the same, add to the queue. */
+							if (oldDpid.equals(dpid)) {
+								outQueue.add(oldFlowMod);
+								outQueue.add(addTmp); 
+								/* Otherwise, go ahead and send the flows now (since queuing them will send to the wrong switch). */
+							} else {
+								writeOFMessageToSwitch(DatapathId.of(oldDpid), oldFlowMod);
+								writeOFMessageToSwitch(DatapathId.of(dpid), FlowModUtils.toFlowAdd(newFlowMod)); 
+							}
+							entriesFromStorage.get(dpid).put(entry, addTmp);
+							entry2dpid.put(entry, dpid);			
 						}
+						/* Add a brand-new flow with ADD */
+					} else if (newFlowMod != null && oldFlowMod == null) {
+						log.debug("Add SFP Flow");
+						OFFlowAdd addTmp = FlowModUtils.toFlowAdd(newFlowMod);
 						entriesFromStorage.get(dpid).put(entry, addTmp);
-						entry2dpid.put(entry, dpid);			
+						entry2dpid.put(entry, dpid);
+						outQueue.add(addTmp);
+						/* Something strange happened, so remove the flow */
+					} else if (newFlowMod == null) { 
+						entriesFromStorage.get(dpid).remove(entry);
+						entry2dpid.remove(entry);
+					}
+				} else { /* must be a group mod */
+					if (oldDpid != null) {
+						oldGroupMod = (OFGroupMod) entriesFromStorage.get(oldDpid).remove(entry);
+					}
+
+					if (oldGroupMod != null && newGroupMod != null) {
+						/* Modify */
+						if (oldGroupMod.getGroup().equals(newGroupMod.getGroup()) &&
+								oldDpid.equalsIgnoreCase(dpid)) {
+							log.debug("Modify SFP Group");
+							entriesFromStorage.get(dpid).put(entry, newGroupMod);
+							entry2dpid.put(entry, dpid);
+							newGroupMod = GroupUtils.toGroupModify(newGroupMod);
+							outQueue.add(newGroupMod);
+						} else { /* Delete followed by Add */
+							log.debug("Delete and Add SFP Group");
+							oldGroupMod = GroupUtils.toGroupDelete(oldGroupMod);
+							newGroupMod = GroupUtils.toGroupAdd(newGroupMod);
+							if (oldDpid.equalsIgnoreCase(dpid)) {
+								outQueue.add(oldGroupMod);
+								outQueue.add(newGroupMod);
+							} else {
+								writeOFMessageToSwitch(DatapathId.of(oldDpid), oldGroupMod);
+								writeOFMessageToSwitch(DatapathId.of(dpid), newGroupMod);
+							}
+							entriesFromStorage.get(dpid).put(entry, newGroupMod);
+							entry2dpid.put(entry, dpid);
+						}
+						/* Add */
+					} else if (oldGroupMod == null && newGroupMod != null) {
+						log.debug("Add SFP Group");
+						newGroupMod = GroupUtils.toGroupAdd(newGroupMod);
+						entriesFromStorage.get(dpid).put(entry, newGroupMod);
+						entry2dpid.put(entry, dpid);
+						outQueue.add(newGroupMod);
+					} else {
+						/* Something strange happened; remove group */
+						entriesFromStorage.get(dpid).remove(entry);
+						entry2dpid.remove(entry);
 					}
-					/* Add a brand-new flow with ADD */
-				} else if (newFlowMod != null && oldFlowMod == null) {
-					log.debug("Add SFP Flow");
-					OFFlowAdd addTmp = FlowModUtils.toFlowAdd(newFlowMod);
-					entriesFromStorage.get(dpid).put(entry, addTmp);
-					entry2dpid.put(entry, dpid);
-					outQueue.add(addTmp);
-					/* Something strange happened, so remove the flow */
-				} else if (newFlowMod == null) { 
-					entriesFromStorage.get(dpid).remove(entry);
-					entry2dpid.remove(entry);
 				}
 			}
 			/* Batch-write all queued messages to the switch */
@@ -582,7 +614,12 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 
 		// send flow_mod delete
 		if (switchService.getSwitch(DatapathId.of(dpid)) != null) {
-			OFFlowDeleteStrict flowMod = FlowModUtils.toFlowDeleteStrict(entriesFromStorage.get(dpid).get(entryName));
+			OFMessage message = entriesFromStorage.get(dpid).get(entryName);
+			if (message instanceof OFFlowMod) {
+				message = FlowModUtils.toFlowDeleteStrict((OFFlowMod) message);
+			} else if (message instanceof OFGroupMod) {
+				message = GroupUtils.toGroupDelete((OFGroupMod) message);
+			}
 
 			if (entriesFromStorage.containsKey(dpid) && entriesFromStorage.get(dpid).containsKey(entryName)) {
 				entriesFromStorage.get(dpid).remove(entryName);
@@ -591,7 +628,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 				return;
 			}
 
-			writeFlowModToSwitch(DatapathId.of(dpid), flowMod);
+			writeOFMessageToSwitch(DatapathId.of(dpid), message);
 		} else {
 			log.debug("Not sending flow delete for disconnected switch.");
 		}
@@ -628,34 +665,9 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		}
 	}
 
-	/**
-	 * Writes an OFFlowMod to a switch. It checks to make sure the switch
-	 * exists before it sends
-	 * @param dpid The data  to write the flow mod to
-	 * @param flowMod The OFFlowMod to write
-	 */
-	private void writeFlowModToSwitch(DatapathId dpid, OFFlowMod flowMod) {
-		IOFSwitch ofSwitch = switchService.getSwitch(dpid);
-		if (ofSwitch == null) {
-			if (log.isDebugEnabled()) {
-				log.debug("Not deleting key {} :: switch {} not connected", dpid.toString());
-			}
-			return;
-		}
-		writeFlowModToSwitch(ofSwitch, flowMod);
-	}
-
-	/**
-	 * Writes an OFFlowMod to a switch
-	 * @param sw The IOFSwitch to write to
-	 * @param flowMod The OFFlowMod to write
-	 */
-	private void writeFlowModToSwitch(IOFSwitch sw, OFFlowMod flowMod) {
-		sw.write(flowMod);
-	}
 	@Override
 	public String getName() {
-		return StaticFlowName;
+		return MODULE_NAME;
 	}
 
 	/**
@@ -669,74 +681,60 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	 */
 	public Command handleFlowRemoved(IOFSwitch sw, OFFlowRemoved msg, FloodlightContext cntx) {
 		U64 cookie = msg.getCookie();
-		/**
-		 * This is just to sanity check our assumption that static flows
-		 * never expire.
-		 */
-		if (AppCookie.extractApp(cookie) == STATIC_FLOW_APP_ID) {
+
+		if (AppCookie.extractApp(cookie) == STATIC_ENTRY_APP_ID) {
 			OFFlowRemovedReason reason = null;
-			switch (msg.getVersion()) {
-			case OF_10:
-				reason = OFFlowRemovedReasonSerializerVer10.ofWireValue((byte) msg.getReason());
-				break;
-			case OF_11:
-				reason = OFFlowRemovedReasonSerializerVer11.ofWireValue((byte) msg.getReason());
-				break;
-			case OF_12:
-				reason = OFFlowRemovedReasonSerializerVer12.ofWireValue((byte) msg.getReason());
-				break;
-			case OF_13:
-				reason = OFFlowRemovedReasonSerializerVer13.ofWireValue((byte) msg.getReason());
-				break;
-			case OF_14:
-				reason = OFFlowRemovedReasonSerializerVer14.ofWireValue((byte) msg.getReason());
-				break;
-			default:
-				log.debug("OpenFlow version {} unsupported for OFFlowRemovedReasonSerializerVerXX", msg.getVersion());
-				break;
-			}
+			reason = msg.getReason();
 			if (reason != null) {
 				if (OFFlowRemovedReason.DELETE == reason) {
-					log.error("Got a FlowRemove message for a infinite " + 
-							"timeout flow: {} from switch {}", msg, sw);
+					log.debug("Received flow_removed message for a infinite " + 
+							"timeout flow from switch {}. Removing it from the SFP DB", msg, sw);
 				} else if (OFFlowRemovedReason.HARD_TIMEOUT == reason || OFFlowRemovedReason.IDLE_TIMEOUT == reason) {
 					/* Remove the Flow from the DB since it timed out */
-					log.debug("Received an IDLE or HARD timeout for an SFP flow. Removing it from the SFP DB.");
-					/* 
-					 * Lookup the flow based on the flow contents. We do not know/care about the name of the 
-					 * flow based on this message, but we can get the table values for this switch and search.
-					 */
-					String flowToRemove = null;
-					Map<String, OFFlowMod> flowsByName = getFlows(sw.getId());
-					for (Map.Entry<String, OFFlowMod> entry : flowsByName.entrySet()) {
-						if (msg.getCookie().equals(entry.getValue().getCookie()) &&
-								(msg.getVersion().compareTo(OFVersion.OF_12) < 0 ? true : msg.getHardTimeout() == entry.getValue().getHardTimeout()) &&
-								msg.getIdleTimeout() == entry.getValue().getIdleTimeout() &&
-								msg.getMatch().equals(entry.getValue().getMatch()) &&
-								msg.getPriority() == entry.getValue().getPriority() &&
-								(msg.getVersion().compareTo(OFVersion.OF_10) == 0 ? true : msg.getTableId().equals(entry.getValue().getTableId()))
-								) {
-							flowToRemove = entry.getKey();
-							break;
-						}
+					log.debug("Received an IDLE or HARD timeout for an SFP flow. Removing it from the SFP DB");
+				} else {
+					log.debug("Received flow_removed message for reason {}. Removing it from the SFP DB", reason);
+				}
+				/* 
+				 * Lookup the flow based on the flow contents. We do not know/care about the name of the 
+				 * flow based on this message, but we can get the table values for this switch and search.
+				 */
+				String flowToRemove = null;
+				Map<String, OFMessage> flowsByName = getEntries(sw.getId())
+						.entrySet()
+						.stream()
+						.filter(e -> e.getValue() instanceof OFFlowMod)
+						.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
+
+				for (Map.Entry<String, OFMessage> e : flowsByName.entrySet()) {
+					OFFlowMod f = (OFFlowMod) e.getValue();
+					if (msg.getCookie().equals(f.getCookie()) &&
+							(msg.getVersion().compareTo(OFVersion.OF_12) < 0 ? true : msg.getHardTimeout() == f.getHardTimeout()) &&
+							msg.getIdleTimeout() == f.getIdleTimeout() &&
+							msg.getMatch().equals(f.getMatch()) &&
+							msg.getPriority() == f.getPriority() &&
+							(msg.getVersion().compareTo(OFVersion.OF_10) == 0 ? true : msg.getTableId().equals(f.getTableId()))
+							) {
+						flowToRemove = e.getKey();
+						break;
 					}
+				}
 
-					log.debug("Flow to Remove: {}", flowToRemove);
-
-					/*
-					 * Remove the flow. This will send the delete message to the switch,
-					 * since we cannot tell the storage listener rowsdeleted() that we
-					 * are only removing our local DB copy of the flow and that it actually
-					 * timed out on the switch and is already gone. The switch will silently
-					 * discard the delete message in this case.
-					 * 
-					 * TODO: We should come up with a way to convey to the storage listener
-					 * the reason for the flow being removed.
-					 */
-					if (flowToRemove != null) {
-						deleteFlow(flowToRemove);
-					}
+				/*
+				 * Remove the flow. This will send the delete message to the switch,
+				 * since we cannot tell the storage listener rowsdeleted() that we
+				 * are only removing our local DB copy of the flow and that it actually
+				 * timed out on the switch and is already gone. The switch will silently
+				 * discard the delete message in this case.
+				 * 
+				 * TODO: We should come up with a way to convey to the storage listener
+				 * the reason for the flow being removed.
+				 */
+				if (flowToRemove != null) {
+					log.warn("Removing flow {} for reason {}", flowToRemove, reason);
+					deleteEntry(flowToRemove);
 				}
+
 				/* Stop the processing chain since we sent or asked for the delete message. */
 				return Command.STOP;
 			}
@@ -771,7 +769,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
 		Collection<Class<? extends IFloodlightService>> l =
 				new ArrayList<Class<? extends IFloodlightService>>();
-		l.add(IStaticFlowEntryPusherService.class);
+		l.add(IStaticEntryPusherService.class);
 		return l;
 	}
 
@@ -781,7 +779,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		IFloodlightService> m =
 		new HashMap<Class<? extends IFloodlightService>,
 		IFloodlightService>();
-		m.put(IStaticFlowEntryPusherService.class, this);
+		m.put(IStaticEntryPusherService.class, this);
 		return m;
 	}
 
@@ -796,8 +794,43 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		return l;
 	}
 
+	private void populateColumns() {
+		Set<String> tmp = new HashSet<String>();
+		tmp.add(Columns.COLUMN_NAME);
+		tmp.add(Columns.COLUMN_ENTRY_TYPE);
+		tmp.add(Columns.COLUMN_SWITCH);
+		tmp.add(Columns.COLUMN_TABLE_ID);
+		tmp.add(Columns.COLUMN_ACTIVE);
+		tmp.add(Columns.COLUMN_IDLE_TIMEOUT);
+		tmp.add(Columns.COLUMN_HARD_TIMEOUT);
+		tmp.add(Columns.COLUMN_PRIORITY);
+		tmp.add(Columns.COLUMN_COOKIE);
+		tmp.add(Columns.COLUMN_TP_SRC);
+		tmp.add(Columns.COLUMN_TP_DST);
+		tmp.add(Columns.COLUMN_ACTIONS);
+		
+		tmp.add(Columns.COLUMN_GROUP_TYPE);
+		tmp.add(Columns.COLUMN_GROUP_BUCKETS);
+		tmp.add(Columns.COLUMN_GROUP_ID);
+		
+		for (MatchFields m : MatchFields.values()) {
+			/* skip all BSN_* matches */
+			if (!m.name().toLowerCase().startsWith("bsn")) {
+				tmp.add(StaticEntryPusher.matchFieldToColumnName(m));
+			}
+		}
+		
+		for (OFInstructionType t : OFInstructionType.values()) {
+			tmp.add(StaticEntryPusher.intructionToColumnName(t));
+		}
+
+		Columns.ALL_COLUMNS = ImmutableSet.copyOf(tmp);
+		
+	}
+	
 	@Override
 	public void init(FloodlightModuleContext context) throws FloodlightModuleException {
+		populateColumns();
 		floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class);
 		switchService = context.getServiceImpl(IOFSwitchService.class);
 		storageSourceService = context.getServiceImpl(IStorageSourceService.class);
@@ -812,11 +845,13 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		floodlightProviderService.addHAListener(this.haListener);
 		// assumes no switches connected at startup()
 		storageSourceService.createTable(TABLE_NAME, null);
-		storageSourceService.setTablePrimaryKeyName(TABLE_NAME, COLUMN_NAME);
+		storageSourceService.setTablePrimaryKeyName(TABLE_NAME, Columns.COLUMN_NAME);
 		storageSourceService.addListener(TABLE_NAME, this);
 		entriesFromStorage = readEntriesFromStorage();
 		entry2dpid = computeEntry2DpidMap(entriesFromStorage);
-		restApiService.addRestletRoutable(new StaticFlowEntryWebRoutable());
+		restApiService.addRestletRoutable(new StaticEntryWebRoutable()); /* current */
+	    restApiService.addRestletRoutable(new StaticFlowWebRoutable()); /* v1.0 - v1.2 (v1.3?) */
+	    restApiService.addRestletRoutable(new StaticFlowEntryWebRoutable()); /* v0.91, v0.90, and before */
 	}
 
 	// IStaticFlowEntryPusherService methods
@@ -824,23 +859,32 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	@Override
 	public void addFlow(String name, OFFlowMod fm, DatapathId swDpid) {
 		try {
-			Map<String, Object> fmMap = StaticFlowEntries.flowModToStorageEntry(fm, swDpid.toString(), name);
+			Map<String, Object> fmMap = StaticEntries.flowModToStorageEntry(fm, swDpid.toString(), name);
 			storageSourceService.insertRowAsync(TABLE_NAME, fmMap);
 		} catch (Exception e) {
-			log.error("Error! Check the fields specified for the flow.Make sure IPv4 fields are not mixed with IPv6 fields or all "
-					+ "mandatory fields are specified. ");
+			log.error("Did not add flow with bad match/action combination. {}", fm);
+		}
+	}
+
+	@Override
+	public void addGroup(String name, OFGroupMod gm, DatapathId swDpid) {
+		try {
+			Map<String, Object> gmMap = StaticEntries.groupModToStorageEntry(gm, swDpid.toString(), name);
+			storageSourceService.insertRowAsync(TABLE_NAME, gmMap);
+		} catch (Exception e) {
+			log.error("Did not add group with bad match/action combination. {}", gm);
 		}
 	}
 
 	@Override
-	public void deleteFlow(String name) {
+	public void deleteEntry(String name) {
 		storageSourceService.deleteRowAsync(TABLE_NAME, name);
 	}
 
 	@Override
-	public void deleteAllFlows() {
+	public void deleteAllEntries() {
 		for (String entry : entry2dpid.keySet()) {
-			deleteFlow(entry);
+			deleteEntry(entry);
 		}
 
 		/*
@@ -873,12 +917,12 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	}
 
 	@Override
-	public void deleteFlowsForSwitch(DatapathId dpid) {
+	public void deleteEntriesForSwitch(DatapathId dpid) {
 		String sDpid = dpid.toString();
 
 		for (Entry<String, String> e : entry2dpid.entrySet()) {
 			if (e.getValue().equals(sDpid))
-				deleteFlow(e.getKey());
+				deleteEntry(e.getKey());
 		}
 
 		/*
@@ -947,13 +991,14 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	 */
 
 	@Override
-	public Map<String, Map<String, OFFlowMod>> getFlows() {
+	public Map<String, Map<String, OFMessage>> getEntries() {
 		return entriesFromStorage;
 	}
 
 	@Override
-	public Map<String, OFFlowMod> getFlows(DatapathId dpid) {
-		return entriesFromStorage.get(dpid.toString());
+	public Map<String, OFMessage> getEntries(DatapathId dpid) {
+		Map<String, OFMessage> m = entriesFromStorage.get(dpid.toString());
+		return m == null ? Collections.emptyMap() : m;
 	}
 
 	// IHAListener
@@ -977,7 +1022,7 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 
 		@Override
 		public String getName() {
-			return StaticFlowEntryPusher.this.getName();
+			return StaticEntryPusher.this.getName();
 		}
 
 		@Override
@@ -995,13 +1040,11 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 		@Override
 		public void transitionToStandby() {	
 			log.debug("Controller is now in STANDBY role. Clearing static flow entries from store.");
-			deleteAllFlows();
+			deleteAllEntries();
 		}
 	}
 
 	@Override
 	public void switchDeactivated(DatapathId switchId) {
-		// TODO Auto-generated method stub
-		
 	}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/ClearStaticFlowEntriesResource.java b/src/main/java/net/floodlightcontroller/staticentry/web/ClearStaticEntriesResource.java
similarity index 60%
rename from src/main/java/net/floodlightcontroller/staticflowentry/web/ClearStaticFlowEntriesResource.java
rename to src/main/java/net/floodlightcontroller/staticentry/web/ClearStaticEntriesResource.java
index 61a02fb170f85d18393412e0ae496c5d13d13620..a51fee3d3313a27302808c42f8eeef82433a751c 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/web/ClearStaticFlowEntriesResource.java
+++ b/src/main/java/net/floodlightcontroller/staticentry/web/ClearStaticEntriesResource.java
@@ -14,10 +14,10 @@
  *    under the License.
  **/
 
-package net.floodlightcontroller.staticflowentry.web;
+package net.floodlightcontroller.staticentry.web;
 
 import net.floodlightcontroller.core.web.ControllerSwitchesResource;
-import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
+import net.floodlightcontroller.staticentry.IStaticEntryPusherService;
 
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.restlet.data.Status;
@@ -26,30 +26,30 @@ import org.restlet.resource.ServerResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ClearStaticFlowEntriesResource extends ServerResource {
-    protected static Logger log = LoggerFactory.getLogger(ClearStaticFlowEntriesResource.class);
+public class ClearStaticEntriesResource extends ServerResource {
+    protected static Logger log = LoggerFactory.getLogger(ClearStaticEntriesResource.class);
     
     @Get("json")
-    public String ClearStaticFlowEntries() {
-        IStaticFlowEntryPusherService sfpService =
-                (IStaticFlowEntryPusherService)getContext().getAttributes().
-                    get(IStaticFlowEntryPusherService.class.getCanonicalName());
+    public String ClearStaticEntries() {
+        IStaticEntryPusherService sfpService =
+                (IStaticEntryPusherService)getContext().getAttributes().
+                    get(IStaticEntryPusherService.class.getCanonicalName());
         
         String param = (String) getRequestAttributes().get("switch");
         if (log.isDebugEnabled())
-            log.debug("Clearing all static flow entires for switch: " + param);
+            log.debug("Clearing all static flow/group entires for switch: " + param);
         
         if (param.toLowerCase().equals("all")) {
-            sfpService.deleteAllFlows();
-            return "{\"status\":\"Deleted all flows.\"}";
+            sfpService.deleteAllEntries();
+            return "{\"status\":\"Deleted all flows/groups.\"}";
         } else {
             try {
-                sfpService.deleteFlowsForSwitch(DatapathId.of(param));
-                return "{\"status\":\"Deleted all flows for switch " + param + ".\"}";
+                sfpService.deleteEntriesForSwitch(DatapathId.of(param));
+                return "{\"status\":\"Deleted all flows/groups for switch " + param + ".\"}";
             } catch (NumberFormatException e){
                 setStatus(Status.CLIENT_ERROR_BAD_REQUEST, 
                           ControllerSwitchesResource.DPID_ERROR);
-                return "'{\"status\":\"Could not delete flows requested! See controller log for details.\"}'";
+                return "'{\"status\":\"Could not delete flows/groups requested! See controller log for details.\"}'";
             }
         }
     }
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/ListStaticFlowEntriesResource.java b/src/main/java/net/floodlightcontroller/staticentry/web/ListStaticEntriesResource.java
similarity index 61%
rename from src/main/java/net/floodlightcontroller/staticflowentry/web/ListStaticFlowEntriesResource.java
rename to src/main/java/net/floodlightcontroller/staticentry/web/ListStaticEntriesResource.java
index 3c72ed40b894480b99a1472e5d019e16b863dc79..1dc61e955cf941a6d6f76a45f55a6affe4276d78 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/web/ListStaticFlowEntriesResource.java
+++ b/src/main/java/net/floodlightcontroller/staticentry/web/ListStaticEntriesResource.java
@@ -14,15 +14,15 @@
  *    under the License.
  **/
 
-package net.floodlightcontroller.staticflowentry.web;
+package net.floodlightcontroller.staticentry.web;
 
 import java.util.HashMap;
 import java.util.Map;
 
 import net.floodlightcontroller.core.web.ControllerSwitchesResource;
-import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
+import net.floodlightcontroller.staticentry.IStaticEntryPusherService;
 
-import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.restlet.data.Status;
 import org.restlet.resource.Get;
@@ -30,26 +30,26 @@ import org.restlet.resource.ServerResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ListStaticFlowEntriesResource extends ServerResource {
-    protected static Logger log = LoggerFactory.getLogger(ListStaticFlowEntriesResource.class);
+public class ListStaticEntriesResource extends ServerResource {
+    protected static Logger log = LoggerFactory.getLogger(ListStaticEntriesResource.class);
     
     @Get("json")
-    public OFFlowModMap ListStaticFlowEntries() {
-        IStaticFlowEntryPusherService sfpService =
-                (IStaticFlowEntryPusherService)getContext().getAttributes().
-                    get(IStaticFlowEntryPusherService.class.getCanonicalName());
+    public SFPEntryMap ListStaticFlowEntries() {
+        IStaticEntryPusherService sfpService =
+                (IStaticEntryPusherService)getContext().getAttributes().
+                    get(IStaticEntryPusherService.class.getCanonicalName());
         
         String param = (String) getRequestAttributes().get("switch");
         if (log.isDebugEnabled())
-            log.debug("Listing all static flow entires for switch: " + param);
+            log.debug("Listing all static flow/group entires for switch: " + param);
         
         if (param.toLowerCase().equals("all")) {
-            return new OFFlowModMap(sfpService.getFlows());
+            return new SFPEntryMap(sfpService.getEntries());
         } else {
             try {
-                Map<String, Map<String, OFFlowMod>> retMap = new HashMap<String, Map<String, OFFlowMod>>();
-                retMap.put(param, sfpService.getFlows(DatapathId.of(param)));
-                return new OFFlowModMap(retMap);
+                Map<String, Map<String, OFMessage>> retMap = new HashMap<String, Map<String, OFMessage>>();
+                retMap.put(param, sfpService.getEntries(DatapathId.of(param)));
+                return new SFPEntryMap(retMap);
                 
             } catch (NumberFormatException e){
                 setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ControllerSwitchesResource.DPID_ERROR);
diff --git a/src/main/java/net/floodlightcontroller/staticentry/web/SFPEntryMap.java b/src/main/java/net/floodlightcontroller/staticentry/web/SFPEntryMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..1bc929c7a46ae43a0800a7517f6f0daad48400ec
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/staticentry/web/SFPEntryMap.java
@@ -0,0 +1,25 @@
+package net.floodlightcontroller.staticentry.web;
+
+import java.util.Map;
+
+import org.projectfloodlight.openflow.protocol.OFMessage;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+@JsonSerialize(using=SFPEntryMapSerializer.class) 
+public class SFPEntryMap {
+
+	/*
+	 * Contains the following double-mapping:
+	 * Map<Switch-DPID-Str, Map<Entry-Name-Str, OFMessage>>
+	 */
+	private Map<String, Map<String, OFMessage>> theMap;
+	
+	public SFPEntryMap (Map<String, Map<String, OFMessage>> theMap) {
+		this.theMap = theMap;
+	}
+	
+	public Map<String, Map<String, OFMessage>> getMap() {
+		return theMap;
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/OFFlowModMapSerializer.java b/src/main/java/net/floodlightcontroller/staticentry/web/SFPEntryMapSerializer.java
similarity index 67%
rename from src/main/java/net/floodlightcontroller/staticflowentry/web/OFFlowModMapSerializer.java
rename to src/main/java/net/floodlightcontroller/staticentry/web/SFPEntryMapSerializer.java
index 368716790868845ded5ab967568fc02d81d5df8e..7f5af0c9056ba9652e81a8aef300e00857d4ff60 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/web/OFFlowModMapSerializer.java
+++ b/src/main/java/net/floodlightcontroller/staticentry/web/SFPEntryMapSerializer.java
@@ -1,11 +1,14 @@
-package net.floodlightcontroller.staticflowentry.web;
+package net.floodlightcontroller.staticentry.web;
 
 import java.io.IOException;
 import java.util.Map;
 
 import net.floodlightcontroller.core.web.serializers.OFFlowModSerializer;
+import net.floodlightcontroller.core.web.serializers.OFGroupModSerializer;
 
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+import org.projectfloodlight.openflow.protocol.OFMessage;
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -25,22 +28,22 @@ import com.fasterxml.jackson.databind.SerializerProvider;
  * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
  *
  */
-public class OFFlowModMapSerializer extends JsonSerializer<OFFlowModMap> {
+public class SFPEntryMapSerializer extends JsonSerializer<SFPEntryMap> {
 
 	@Override
-	public void serialize(OFFlowModMap fmm, JsonGenerator jGen, SerializerProvider serializer)
+	public void serialize(SFPEntryMap em, JsonGenerator jGen, SerializerProvider serializer)
 			throws IOException, JsonProcessingException {
 		
         jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); // IMHO this just looks nicer and is easier to read if everything is quoted
 
-		if (fmm == null) {
+		if (em == null) {
 			jGen.writeStartObject();
 			jGen.writeString("No flows have been added to the Static Flow Pusher.");
 			jGen.writeEndObject();
 			return;
 		}
 
-		Map<String, Map<String, OFFlowMod>> theMap = fmm.getMap();
+		Map<String, Map<String, OFMessage>> theMap = em.getMap();
 
 		jGen.writeStartObject();
 		if (theMap.keySet() != null) {
@@ -50,7 +53,11 @@ public class OFFlowModMapSerializer extends JsonSerializer<OFFlowModMap> {
 					for (String name : theMap.get(dpid).keySet()) {
 						jGen.writeStartObject();
 						jGen.writeFieldName(name);
-						OFFlowModSerializer.serializeFlowMod(jGen, theMap.get(dpid).get(name));
+						if (theMap.get(dpid).get(name) instanceof OFFlowMod) {
+							OFFlowModSerializer.serializeFlowMod(jGen, (OFFlowMod) theMap.get(dpid).get(name));
+						} else if (theMap.get(dpid).get(name) instanceof OFGroupMod) {
+							OFGroupModSerializer.serializeGroupMod(jGen, (OFGroupMod) theMap.get(dpid).get(name));
+						}
 						jGen.writeEndObject();
 					}    
 					jGen.writeEndArray();
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryPusherResource.java b/src/main/java/net/floodlightcontroller/staticentry/web/StaticEntryPusherResource.java
similarity index 62%
rename from src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryPusherResource.java
rename to src/main/java/net/floodlightcontroller/staticentry/web/StaticEntryPusherResource.java
index f46c3f94bbc6c296ca8a4b50315a07fba8e6716d..9e189cd02702faeeda1bc026fe78ec2a86a51f7a 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryPusherResource.java
+++ b/src/main/java/net/floodlightcontroller/staticentry/web/StaticEntryPusherResource.java
@@ -15,11 +15,13 @@
  *    under the License.
  **/
 
-package net.floodlightcontroller.staticflowentry.web;
+package net.floodlightcontroller.staticentry.web;
 
 import java.io.IOException;
 import java.util.Map;
 
+import org.projectfloodlight.openflow.protocol.OFInstructionType;
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.restlet.resource.Delete;
 import org.restlet.resource.Post;
@@ -27,18 +29,16 @@ import org.restlet.resource.ServerResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import net.floodlightcontroller.staticflowentry.StaticFlowEntries;
-import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
+import net.floodlightcontroller.staticentry.StaticEntries;
+import net.floodlightcontroller.staticentry.StaticEntryPusher;
 import net.floodlightcontroller.storage.IStorageSourceService;
 import net.floodlightcontroller.util.MatchUtils;
 
 /**
  * Pushes a static flow entry to the storage source
- * @author alexreimers
- *
  */
-public class StaticFlowEntryPusherResource extends ServerResource {
-	protected static Logger log = LoggerFactory.getLogger(StaticFlowEntryPusherResource.class);
+public class StaticEntryPusherResource extends ServerResource {
+	protected static Logger log = LoggerFactory.getLogger(StaticEntryPusherResource.class);
 
 	/**
 	 * Validates if all the mandatory fields are set properly while adding an IPv6 flow
@@ -64,12 +64,12 @@ public class StaticFlowEntryPusherResource extends ServerResource {
 		int icmp_type = -1;
 		
 		//Determine the dl_type if set
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_DL_TYPE)) {
-			if (((String) rows.get(StaticFlowEntryPusher.COLUMN_DL_TYPE)).startsWith("0x")) {
-				eth_type = Integer.parseInt(((String) rows.get(StaticFlowEntryPusher.COLUMN_DL_TYPE)).replaceFirst("0x", ""), 16);
+		if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE))) {
+			if (((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE))).startsWith("0x")) {
+				eth_type = Integer.parseInt(((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE))).replaceFirst("0x", ""), 16);
 				dl_type = true;
 			} else {
-				eth_type = Integer.parseInt((String) rows.get(StaticFlowEntryPusher.COLUMN_DL_TYPE));
+				eth_type = Integer.parseInt((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE)));
 				dl_type = true;
 			}
 			if (eth_type == 0x86dd) { /* or 34525 */
@@ -86,56 +86,61 @@ public class StaticFlowEntryPusherResource extends ServerResource {
 			//	return state;
 			//}
 		}
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_NW_DST) ||
-				rows.containsKey(StaticFlowEntryPusher.COLUMN_NW_SRC)) {
+		final String ipv4_dst_str = StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV4_DST);
+		final String ipv4_src_str = StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV4_SRC);
+		if (rows.containsKey(ipv4_dst_str) ||
+				rows.containsKey(ipv4_src_str)) {
 			nw_layer = true;
 			ip4 = true;
 		}
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_ICMP_CODE) ||
-				rows.containsKey(StaticFlowEntryPusher.COLUMN_ICMP_TYPE) ||
-				rows.containsKey(StaticFlowEntryPusher.COLUMN_ARP_DHA) ||
-				rows.containsKey(StaticFlowEntryPusher.COLUMN_ARP_SHA) ||
-				rows.containsKey(StaticFlowEntryPusher.COLUMN_ARP_SPA) ||
-				rows.containsKey(StaticFlowEntryPusher.COLUMN_ARP_DPA) ||
-				rows.containsKey(StaticFlowEntryPusher.COLUMN_ARP_OPCODE)) {
+		
+		if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV4_CODE)) ||
+				rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV4_TYPE)) ||
+				rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ARP_THA)) ||
+				rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ARP_SHA)) ||
+				rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ARP_TPA)) ||
+				rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ARP_SPA)) ||
+				rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ARP_OP))) {
 			ip4 = true;
 		}
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL) || 
-				rows.containsKey(StaticFlowEntryPusher.COLUMN_NW6_SRC) ||
-				rows.containsKey(StaticFlowEntryPusher.COLUMN_NW6_DST)) {
+		if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_FLABEL)) || 
+				rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_SRC)) ||
+				rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_DST))) {
 			nw_layer = true;
 			ip6 = true;
 		}
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_NW_PROTO)) {
+		if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IP_PROTO))) {
 			nw_proto = true;
-			if (((String) rows.get(StaticFlowEntryPusher.COLUMN_NW_PROTO)).startsWith("0x")) {
-				nw_protocol = Integer.parseInt(((String) rows.get(StaticFlowEntryPusher.COLUMN_NW_PROTO)).replaceFirst("0x", ""), 16);
+			if (((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.IP_PROTO))).startsWith("0x")) {
+				nw_protocol = Integer.parseInt(((String) rows.get(StaticEntryPusher
+						.matchFieldToColumnName(MatchFields.IP_PROTO))).replaceFirst("0x", ""), 16);
 			} else {
-				nw_protocol = Integer.parseInt((String) rows.get(StaticFlowEntryPusher.COLUMN_NW_PROTO));
+				nw_protocol = Integer.parseInt((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.IP_PROTO)));
 			}
 		}
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_ICMP6_CODE)) {
+		if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV6_CODE))) {
 			icmp6_code = true;
 			ip6 = true;
 		}
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE)) {
+		if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV6_TYPE))) {
 			icmp6_type = true;
 			ip6 = true;
-			if (((String) rows.get(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE)).startsWith("0x")) {
-				icmp_type = Integer.parseInt(((String) rows.get(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE)).replaceFirst("0x", ""), 16);
+			if (((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV6_TYPE))).startsWith("0x")) {
+				icmp_type = Integer.parseInt(((String) rows.get(StaticEntryPusher
+						.matchFieldToColumnName(MatchFields.ICMPV6_TYPE))).replaceFirst("0x", ""), 16);
 			} else {
-				icmp_type = Integer.parseInt((String) rows.get(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE));
+				icmp_type = Integer.parseInt((String) rows.get(StaticEntryPusher.matchFieldToColumnName(MatchFields.ICMPV6_TYPE)));
 			}
 		}
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_ND_SLL)) {
+		if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_ND_SLL))) {
 			nd_sll = true;
 			ip6 = true;
 		}
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_ND_TLL)) {
+		if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_ND_TLL))) {
 			nd_tll = true;
 			ip6 = true;
 		}
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_ND_TARGET)) {
+		if (rows.containsKey(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV6_ND_TARGET))) {
 			nd_target = true;
 			ip6 = true;
 		}    
@@ -212,9 +217,9 @@ public class StaticFlowEntryPusherResource extends ServerResource {
 			return state;
 		}
 		
-		if (rows.containsKey(StaticFlowEntryPusher.COLUMN_SWITCH)) {
+		if (rows.containsKey(StaticEntryPusher.Columns.COLUMN_SWITCH)) {
 			try {
-				DatapathId.of((String) rows.get(StaticFlowEntryPusher.COLUMN_SWITCH));
+				DatapathId.of((String) rows.get(StaticEntryPusher.Columns.COLUMN_SWITCH));
 			} catch (Exception e) {
 				state = 9;
 			}
@@ -241,23 +246,23 @@ public class StaticFlowEntryPusherResource extends ServerResource {
 		boolean ip4 = false;
 		String actions = null;
 
-		if (entry.containsKey(StaticFlowEntryPusher.COLUMN_ACTIONS) || 
-				entry.containsKey(StaticFlowEntryPusher.COLUMN_INSTR_APPLY_ACTIONS) ||
-				entry.containsKey(StaticFlowEntryPusher.COLUMN_INSTR_WRITE_ACTIONS)) {
-			if (entry.containsKey(StaticFlowEntryPusher.COLUMN_ACTIONS)) {
-				actions = (String) entry.get(StaticFlowEntryPusher.COLUMN_ACTIONS);
+		if (entry.containsKey(StaticEntryPusher.Columns.COLUMN_ACTIONS) || 
+				entry.containsKey(StaticEntryPusher.intructionToColumnName(OFInstructionType.APPLY_ACTIONS)) ||
+				entry.containsKey(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_ACTIONS))) {
+			if (entry.containsKey(StaticEntryPusher.Columns.COLUMN_ACTIONS)) {
+				actions = (String) entry.get(StaticEntryPusher.Columns.COLUMN_ACTIONS);
 			}
-			else if (entry.containsKey(StaticFlowEntryPusher.COLUMN_INSTR_APPLY_ACTIONS)) {
-				actions = (String) entry.get(StaticFlowEntryPusher.COLUMN_INSTR_APPLY_ACTIONS);
+			else if (entry.containsKey(StaticEntryPusher.intructionToColumnName(OFInstructionType.APPLY_ACTIONS))) {
+				actions = (String) entry.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.APPLY_ACTIONS));
 			}
-			else if (entry.containsKey(StaticFlowEntryPusher.COLUMN_INSTR_WRITE_ACTIONS)) {
-				actions = (String) entry.get(StaticFlowEntryPusher.COLUMN_INSTR_WRITE_ACTIONS);
+			else if (entry.containsKey(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_ACTIONS))) {
+				actions = (String) entry.get(StaticEntryPusher.intructionToColumnName(OFInstructionType.WRITE_ACTIONS));
 			}
 
 			if (actions.contains(MatchUtils.STR_ICMPV6_CODE) || actions.contains(MatchUtils.STR_ICMPV6_TYPE) ||
 					actions.contains(MatchUtils.STR_IPV6_DST) || actions.contains(MatchUtils.STR_IPV6_SRC) || 
-					actions.contains(MatchUtils.STR_IPV6_FLOW_LABEL) || actions.contains(MatchUtils.STR_IPV6_ND_SSL) ||
-					actions.contains(MatchUtils.STR_IPV6_ND_TARGET) || actions.contains(MatchUtils.STR_IPV6_ND_TTL)) {
+					actions.contains(MatchUtils.STR_IPV6_FLOW_LABEL) || actions.contains(MatchUtils.STR_IPV6_ND_SLL) ||
+					actions.contains(MatchUtils.STR_IPV6_ND_TARGET) || actions.contains(MatchUtils.STR_IPV6_ND_TLL)) {
 				ip6 = true;
 			}
 			if (actions.contains(MatchUtils.STR_NW_SRC) || actions.contains(MatchUtils.STR_NW_DST) || 
@@ -281,20 +286,20 @@ public class StaticFlowEntryPusherResource extends ServerResource {
 	}
 
 	/**
-	 * Takes a Static Flow Pusher string in JSON format and parses it into
+	 * Takes a Static Entry Pusher string in JSON format and parses it into
 	 * our database schema then pushes it to the database.
 	 * @param fmJson The Static Flow Pusher entry in JSON format.
 	 * @return A string status message
 	 */
 	@Post
-	public String store(String fmJson) {
+	public String store(String json) {
 		IStorageSourceService storageSource =
 				(IStorageSourceService)getContext().getAttributes().
 				get(IStorageSourceService.class.getCanonicalName());
 
 		Map<String, Object> rowValues;
 		try {
-			rowValues = StaticFlowEntries.jsonToStorageEntry(fmJson);
+			rowValues = StaticEntries.jsonToStorageEntry(json);
 			String status = null;
 
 			int state = checkFlow(rowValues);
@@ -328,35 +333,35 @@ public class StaticFlowEntryPusherResource extends ServerResource {
 				log.error(status);
 			} else if (state == 0) {
 				status = "Entry pushed";            
-				storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, rowValues);
+				storageSource.insertRowAsync(StaticEntryPusher.TABLE_NAME, rowValues);
 			}
 			return ("{\"status\" : \"" + status + "\"}");
 		} catch (IOException e) {
-			log.error("Error parsing push flow mod request: " + fmJson, e);
+			log.error("Error parsing push flow mod request: " + json, e);
 			return "{\"status\" : \"Error! Could not parse flow mod, see log for details.\"}";
 		}        
 	}
 
 	@Delete
-	public String del(String fmJson) {
+	public String del(String json) {
 		IStorageSourceService storageSource =
 				(IStorageSourceService)getContext().getAttributes().
 				get(IStorageSourceService.class.getCanonicalName());
 		String fmName = null;
-		if (fmJson == null) {
+		if (json == null) {
 			return "{\"status\" : \"Error! No data posted.\"}";
 		}
 		try {
-			fmName = StaticFlowEntries.getEntryNameFromJson(fmJson);
+			fmName = StaticEntries.getEntryNameFromJson(json);
 			if (fmName == null) {
 				return "{\"status\" : \"Error deleting entry, no name provided\"}";
 			}
 		} catch (IOException e) {
-			log.error("Error deleting flow mod request: " + fmJson, e);
+			log.error("Error deleting flow mod request: " + json, e);
 			return "{\"status\" : \"Error deleting entry, see log for details\"}";
 		}
 
-		storageSource.deleteRowAsync(StaticFlowEntryPusher.TABLE_NAME, fmName);
+		storageSource.deleteRowAsync(StaticEntryPusher.TABLE_NAME, fmName);
 		return "{\"status\" : \"Entry " + fmName + " deleted\"}";
 	}
 }
diff --git a/src/main/java/net/floodlightcontroller/staticentry/web/StaticEntryUsageResource.java b/src/main/java/net/floodlightcontroller/staticentry/web/StaticEntryUsageResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6e506ed39542c5893e88c279226a89ae734a889
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/staticentry/web/StaticEntryUsageResource.java
@@ -0,0 +1,205 @@
+/**
+ *    Copyright 2013, Big Switch Networks, Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.staticentry.web;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import net.floodlightcontroller.core.web.ControllerSwitchesResource;
+import net.floodlightcontroller.staticentry.IStaticEntryPusherService;
+import net.floodlightcontroller.util.MatchUtils;
+
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
+import org.projectfloodlight.openflow.protocol.ver10.OFFactoryVer10;
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.restlet.data.Status;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StaticEntryUsageResource extends ServerResource {
+	protected static Logger log = LoggerFactory.getLogger(StaticEntryUsageResource.class);
+
+	private static String usage = null;
+	private static final String TYPE_NONE = "<none>";
+	private static final String TYPE_NUMBER = "<number>";
+	private static final String TYPE_RESERVED_PORT = "<reserved_port>";
+	private static final String TYPE_MACADDR = "<MAC address>";
+	private static final String TYPE_IPV4ADDR = "<IPv4 address>";
+	private static final String TYPE_IPV4ADDR_MASK = "<IPv4 address[/mask]>";
+	private static final String TYPE_IPV6ADDR = "<IPv6 address>";
+	private static final String TYPE_IPV6ADDR_MASK = "<IPv6 address[/mask]>";
+	
+	private static final Match.Builder b10 = OFFactories.getFactory(OFVersion.OF_10).buildMatch();
+	private static final Match.Builder b11 = OFFactories.getFactory(OFVersion.OF_11).buildMatch();
+	private static final Match.Builder b12 = OFFactories.getFactory(OFVersion.OF_12).buildMatch();
+	private static final Match.Builder b13 = OFFactories.getFactory(OFVersion.OF_13).buildMatch();
+	private static final Match.Builder b14 = OFFactories.getFactory(OFVersion.OF_14).buildMatch();
+	private static final Match.Builder b15 = OFFactories.getFactory(OFVersion.OF_15).buildMatch();
+
+	private static final String NOTE_HEX_OR_DEC = "Can be hexadecimal (with leading 0x) or decimal.";
+
+
+	private class SFPMatch {
+		String key =  "";
+		Set<String> value = new HashSet<String>();
+		Set<String> notes = new HashSet<String>();
+		Map<String, String> prereqs = null;
+		Set<OFVersion> ofVersions = null;
+		
+		//TODO hashcode, equals, toString
+	}
+
+	private static void setSupportedOFVersions(Set<OFVersion> supportedOFVersions, MatchField<?> mf) {
+		if (b10.supports(mf)) {
+			supportedOFVersions.add(OFVersion.OF_10);
+		}
+		if (b11.supports(mf)) {
+			supportedOFVersions.add(OFVersion.OF_11);
+		}
+		if (b12.supports(mf)) {
+			supportedOFVersions.add(OFVersion.OF_12);
+		}
+		if (b13.supports(mf)) {
+			supportedOFVersions.add(OFVersion.OF_13);
+		}
+		if (b14.supports(mf)) {
+			supportedOFVersions.add(OFVersion.OF_14);
+		}
+		//TODO add in other versions as they become available
+	}
+	
+	@Get("json")
+	public Object getStaticFlowPusherUsage() {
+		IStaticEntryPusherService sfpService =
+				(IStaticEntryPusherService)getContext().getAttributes().
+				get(IStaticEntryPusherService.class.getCanonicalName());
+
+		Map<String, Set<? extends Object>> outer = new HashMap<String, Set<? extends Object>>();
+		
+		Set<SFPMatch> matches = new HashSet<SFPMatch>();
+		outer.put("matches", matches);
+		
+		
+		for (MatchFields mf : MatchFields.values()) {
+			SFPMatch m = new SFPMatch();
+			switch (mf) {
+			case ARP_OP:
+				m.key = MatchUtils.STR_ARP_OPCODE;
+				m.value.add(TYPE_NUMBER);
+				m.notes.add(NOTE_HEX_OR_DEC);
+				setSupportedOFVersions(m.ofVersions, MatchField.ARP_OP);
+
+				break;
+			case ARP_SHA:
+				break;
+			case ARP_SPA:
+				break;
+			case ARP_THA:
+				break;
+			case ARP_TPA:
+				break;
+			case ETH_DST:
+				break;
+			case ETH_SRC:
+				break;
+			case ETH_TYPE:
+				break;
+			case ICMPV4_CODE:
+				break;
+			case ICMPV4_TYPE:
+				break;
+			case ICMPV6_CODE:
+				break;
+			case ICMPV6_TYPE:
+				break;
+			case IN_PORT:
+				break;
+			case IPV4_DST:
+				break;
+			case IPV4_SRC:
+				break;
+			case IPV6_DST:
+				break;
+			case IPV6_EXTHDR:
+				break;
+			case IPV6_FLABEL:
+				break;
+			case IPV6_ND_SLL:
+				break;
+			case IPV6_ND_TARGET:
+				break;
+			case IPV6_ND_TLL:
+				break;
+			case IPV6_SRC:
+				break;
+			case IP_DSCP:
+				break;
+			case IP_ECN:
+				break;
+			case IP_PROTO:
+				break;
+			case METADATA:
+				break;
+			case MPLS_BOS:
+				break;
+			case MPLS_LABEL:
+				break;
+			case MPLS_TC:
+				break;
+			case PBB_UCA:
+				break;
+			case SCTP_DST:
+				break;
+			case SCTP_SRC:
+				break;
+			case TCP_DST:
+				break;
+			case TCP_SRC:
+				break;
+			case TUNNEL_ID:
+				break;
+			case TUNNEL_IPV4_DST:
+				break;
+			case TUNNEL_IPV4_SRC:
+				break;
+			case UDP_DST:
+				break;
+			case UDP_SRC:
+				break;
+			case VLAN_PCP:
+				break;
+			case VLAN_VID:
+				break;
+			default:
+				log.debug("Skipped match {}", mf);
+				break;
+			}
+		}
+
+		return usage == null ? "" : usage;
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/staticentry/web/StaticEntryWebRoutable.java b/src/main/java/net/floodlightcontroller/staticentry/web/StaticEntryWebRoutable.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6ec2329afbcbc7b0dcce0792a0f2ba4b9d8817d
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/staticentry/web/StaticEntryWebRoutable.java
@@ -0,0 +1,68 @@
+/**
+ *    Copyright 2013, Big Switch Networks, Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.staticentry.web;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+/**
+ * As Floodlight has evolved, the purpose and capabilities 
+ * of the original Static Flow Entry Pusher (SFEP) have changed.
+ * First, many simply referred to the SFEP as Static Flow
+ * Pusher (SFP), which resulted in incorrect API usage. So, we
+ * shortened the API. Now, the SFP/SFEP can do more than flows.
+ * It can also push groups and might also be able to push meters
+ * and other OpenFlow table entries in the future. Thus, the name
+ * SFP is misleading and credits the SFP will less than it's
+ * actually capable of accomplishing. So, the name now is changing
+ * to an even broader Static Entry Pusher (SEP), where "entry" is
+ * vague enough to encompasses flows, groups, and other potential
+ * types.
+ * 
+ * One thing that hasn't been addressed is that the SEP is also
+ * capable of pushing non-static entries. (It relies on entry
+ * removal messages being sent from the switch to the controller
+ * in order to update its internal store.) Such entries have
+ * timeouts configured. IMHO, it's still okay to call the SEP
+ * static though, since this feature isn't used very often at all.
+ */
+public class StaticEntryWebRoutable implements RestletRoutable {
+    /**
+     * Create the Restlet router and bind to the proper resources.
+     */
+    @Override
+    public Restlet getRestlet(Context context) {
+        Router router = new Router(context);
+        
+        router.attach("/json", StaticEntryPusherResource.class); /* v2.0 advertised API */       
+        router.attach("/clear/{switch}/json", ClearStaticEntriesResource.class);
+        router.attach("/list/{switch}/json", ListStaticEntriesResource.class);
+        router.attach("/usage/json", StaticEntryUsageResource.class);      
+        return router;
+    }
+
+    /**
+     * Set the base path for the SEP
+     */
+    @Override
+    public String basePath() {
+        return "/wm/staticentrypusher";
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/staticentry/web/StaticFlowEntryWebRoutable.java b/src/main/java/net/floodlightcontroller/staticentry/web/StaticFlowEntryWebRoutable.java
new file mode 100644
index 0000000000000000000000000000000000000000..960268afe3848ebd42b926444deb592815485e1b
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/staticentry/web/StaticFlowEntryWebRoutable.java
@@ -0,0 +1,67 @@
+/**
+ *    Copyright 2013, Big Switch Networks, Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.staticentry.web;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+/**
+ * As Floodlight has evolved, the purpose and capabilities 
+ * of the original Static Flow Entry Pusher (SFEP) have changed.
+ * First, many simply referred to the SFEP as Static Flow
+ * Pusher (SFP), which resulted in incorrect API usage. So, we
+ * shortened the API. Now, the SFP/SFEP can do more than flows.
+ * It can also push groups and might also be able to push meters
+ * and other OpenFlow table entries in the future. Thus, the name
+ * SFP is misleading and credits the SFP will less than it's
+ * actually capable of accomplishing. So, the name now is changing
+ * to an even broader Static Entry Pusher (SEP), where "entry" is
+ * vague enough to encompasses flows, groups, and other potential
+ * types.
+ * 
+ * One thing that hasn't been addressed is that the SEP is also
+ * capable of pushing non-static entries. (It relies on entry
+ * removal messages being sent from the switch to the controller
+ * in order to update its internal store.) Such entries have
+ * timeouts configured. IMHO, it's still okay to call the SEP
+ * static though, since this feature isn't used very often at all.
+ */
+public class StaticFlowEntryWebRoutable implements RestletRoutable {
+    /**
+     * Create the Restlet router and bind to the proper resources.
+     */
+    @Override
+    public Restlet getRestlet(Context context) {
+        Router router = new Router(context);
+        router.attach("/json", StaticEntryPusherResource.class); /* v0.91, v0.90, and below */
+        router.attach("/clear/{switch}/json", ClearStaticEntriesResource.class);
+        router.attach("/list/{switch}/json", ListStaticEntriesResource.class);
+        router.attach("/usage/json", StaticEntryUsageResource.class);
+        return router;
+    }
+
+    /**
+     * Set the base path for the SFEP
+     */
+    @Override
+    public String basePath() {
+        return "/wm/staticflowentrypusher";
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/staticentry/web/StaticFlowWebRoutable.java b/src/main/java/net/floodlightcontroller/staticentry/web/StaticFlowWebRoutable.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb2782e186257a4b8a0f7815dfde645db12113cb
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/staticentry/web/StaticFlowWebRoutable.java
@@ -0,0 +1,67 @@
+/**
+ *    Copyright 2013, Big Switch Networks, Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.staticentry.web;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+/**
+ * As Floodlight has evolved, the purpose and capabilities 
+ * of the original Static Flow Entry Pusher (SFEP) have changed.
+ * First, many simply referred to the SFEP as Static Flow
+ * Pusher (SFP), which resulted in incorrect API usage. So, we
+ * shortened the API. Now, the SFP/SFEP can do more than flows.
+ * It can also push groups and might also be able to push meters
+ * and other OpenFlow table entries in the future. Thus, the name
+ * SFP is misleading and credits the SFP will less than it's
+ * actually capable of accomplishing. So, the name now is changing
+ * to an even broader Static Entry Pusher (SEP), where "entry" is
+ * vague enough to encompasses flows, groups, and other potential
+ * types.
+ * 
+ * One thing that hasn't been addressed is that the SEP is also
+ * capable of pushing non-static entries. (It relies on entry
+ * removal messages being sent from the switch to the controller
+ * in order to update its internal store.) Such entries have
+ * timeouts configured. IMHO, it's still okay to call the SEP
+ * static though, since this feature isn't used very often at all.
+ */
+public class StaticFlowWebRoutable implements RestletRoutable {
+    /**
+     * Create the Restlet router and bind to the proper resources.
+     */
+    @Override
+    public Restlet getRestlet(Context context) {
+        Router router = new Router(context);
+        router.attach("/json", StaticEntryPusherResource.class); /* backwards compatibility w/ v1.0-v1.2 (v1.3?) */
+        router.attach("/clear/{switch}/json", ClearStaticEntriesResource.class);
+        router.attach("/list/{switch}/json", ListStaticEntriesResource.class);
+        router.attach("/usage/json", StaticEntryUsageResource.class);
+        return router;
+    }
+
+    /**
+     * Set the base path for the SFP
+     */
+    @Override
+    public String basePath() {
+        return "/wm/staticflowpusher";
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java
deleted file mode 100644
index 12f24d8cc4a65c4a10506a37701e9d81c0f86df2..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java
+++ /dev/null
@@ -1,862 +0,0 @@
-/**
- *    Copyright 2013, Big Switch Networks, Inc.
- *
- *    Licensed under the Apache License, Version 2.0 (the "License"); you may
- *    not use this file except in compliance with the License. You may obtain
- *    a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- *    License for the specific language governing permissions and limitations
- *    under the License.
- **/
-
-package net.floodlightcontroller.staticflowentry;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import net.floodlightcontroller.core.util.AppCookie;
-import net.floodlightcontroller.staticflowentry.web.StaticFlowEntryPusherResource;
-import net.floodlightcontroller.util.ActionUtils;
-import net.floodlightcontroller.util.InstructionUtils;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.databind.MappingJsonFactory;
-
-import org.projectfloodlight.openflow.protocol.OFFlowMod;
-import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstructionClearActions;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstructionExperimenter;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstructionMeter;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteActions;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteMetadata;
-import org.projectfloodlight.openflow.protocol.match.Match;
-import org.projectfloodlight.openflow.protocol.match.MatchField;
-import org.projectfloodlight.openflow.types.OFBufferId;
-import org.projectfloodlight.openflow.types.OFPort;
-import org.projectfloodlight.openflow.types.U64;
-
-/**
- * Represents static flow entries to be maintained by the controller on the 
- * switches. 
- */
-public class StaticFlowEntries {
-	protected static Logger log = LoggerFactory.getLogger(StaticFlowEntries.class);
-	private static final int INFINITE_TIMEOUT = 0;
-
-	/**
-	 * This function generates a random hash for the bottom half of the cookie
-	 * 
-	 * @param fm
-	 * @param userCookie
-	 * @param name
-	 * @return A cookie that encodes the application ID and a hash
-	 */
-	public static U64 computeEntryCookie(int userCookie, String name) {
-		// flow-specific hash is next 20 bits LOOK! who knows if this 
-		int prime = 211;
-		int flowHash = 2311;
-		for (int i=0; i < name.length(); i++) {
-			flowHash = flowHash * prime + (int)name.charAt(i);
-		}
-
-		return AppCookie.makeCookie(StaticFlowEntryPusher.STATIC_FLOW_APP_ID, flowHash);
-	}
-
-	/**
-	 * Sets defaults for an OFFlowMod used in the StaticFlowEntryPusher
-	 * @param fm The OFFlowMod to set defaults for
-	 * @param entryName The name of the entry. Used to compute the cookie.
-	 */
-	public static void initDefaultFlowMod(OFFlowMod.Builder fmb, String entryName) {
-		fmb.setIdleTimeout(INFINITE_TIMEOUT) // not setting these would also work
-		.setHardTimeout(INFINITE_TIMEOUT)
-		.setBufferId(OFBufferId.NO_BUFFER)
-		.setOutPort(OFPort.ANY) 
-		.setCookie(computeEntryCookie(0, entryName))
-		.setPriority(Integer.MAX_VALUE)
-		.setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM));
-		return;
-	}
-
-	/**
-	 * Gets the entry name of a flow mod
-	 * @param fmJson The OFFlowMod in a JSON representation
-	 * @return The name of the OFFlowMod, null if not found
-	 * @throws IOException If there was an error parsing the JSON
-	 */
-	public static String getEntryNameFromJson(String fmJson) throws IOException{
-		MappingJsonFactory f = new MappingJsonFactory();
-		JsonParser jp;
-
-		try {
-			jp = f.createParser(fmJson);
-		} catch (JsonParseException e) {
-			throw new IOException(e);
-		}
-
-		jp.nextToken();
-		if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
-			throw new IOException("Expected START_OBJECT");
-		}
-
-		while (jp.nextToken() != JsonToken.END_OBJECT) {
-			if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
-				throw new IOException("Expected FIELD_NAME");
-			}
-
-			String n = jp.getCurrentName();
-			jp.nextToken();
-			if (jp.getText().equals("")) 
-				continue;
-
-			if (n == StaticFlowEntryPusher.COLUMN_NAME)
-				return jp.getText();
-		}
-		return null;
-	}
-
-	/**
-	 * Parses an OFFlowMod (and it's inner Match) to the storage entry format.
-	 * @param fm The FlowMod to parse
-	 * @param sw The switch the FlowMod is going to be installed on
-	 * @param name The name of this static flow entry
-	 * @return A Map representation of the storage entry 
-	 */
-	public static Map<String, Object> flowModToStorageEntry(OFFlowMod fm, String sw, String name) throws Exception {
-		Map<String, Object> entry = new HashMap<String, Object>();
-		entry.put(StaticFlowEntryPusher.COLUMN_NAME, name);
-		entry.put(StaticFlowEntryPusher.COLUMN_SWITCH, sw);
-		entry.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true));
-		entry.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Integer.toString(fm.getPriority()));
-		entry.put(StaticFlowEntryPusher.COLUMN_IDLE_TIMEOUT, Integer.toString(fm.getIdleTimeout()));
-		entry.put(StaticFlowEntryPusher.COLUMN_HARD_TIMEOUT, Integer.toString(fm.getHardTimeout()));
-
-		switch (fm.getVersion()) {
-		case OF_10:
-			if (fm.getActions() != null) {
-				entry.put(StaticFlowEntryPusher.COLUMN_ACTIONS, ActionUtils.actionsToString(fm.getActions(), log));
-			}
-			break;
-		case OF_11:
-		case OF_12:
-		case OF_13:
-		case OF_14:
-		default:
-			// should have a table ID present
-			if (fm.getTableId() != null) { // if not set, then don't worry about it. Default will be set when built and sent to switch
-				entry.put(StaticFlowEntryPusher.COLUMN_TABLE_ID, Short.toString(fm.getTableId().getValue()));
-			}
-			// should have a list of instructions, of which apply and write actions could have sublists of actions
-			if (fm.getInstructions() != null) {
-				List<OFInstruction> instructions = fm.getInstructions();
-				for (OFInstruction inst : instructions) {
-					switch (inst.getType()) {
-					case GOTO_TABLE:
-						entry.put(StaticFlowEntryPusher.COLUMN_INSTR_GOTO_TABLE, InstructionUtils.gotoTableToString(((OFInstructionGotoTable) inst), log));
-						break;
-					case WRITE_METADATA:
-						entry.put(StaticFlowEntryPusher.COLUMN_INSTR_WRITE_METADATA, InstructionUtils.writeMetadataToString(((OFInstructionWriteMetadata) inst), log));
-						break;
-					case WRITE_ACTIONS:
-						entry.put(StaticFlowEntryPusher.COLUMN_INSTR_WRITE_ACTIONS, InstructionUtils.writeActionsToString(((OFInstructionWriteActions) inst), log));
-						break;
-					case APPLY_ACTIONS:
-						entry.put(StaticFlowEntryPusher.COLUMN_INSTR_APPLY_ACTIONS, InstructionUtils.applyActionsToString(((OFInstructionApplyActions) inst), log));
-						break;
-					case CLEAR_ACTIONS:
-						entry.put(StaticFlowEntryPusher.COLUMN_INSTR_CLEAR_ACTIONS, InstructionUtils.clearActionsToString(((OFInstructionClearActions) inst), log));
-						break;
-					case METER:
-						entry.put(StaticFlowEntryPusher.COLUMN_INSTR_GOTO_METER, InstructionUtils.meterToString(((OFInstructionMeter) inst), log));
-						break;
-					case EXPERIMENTER:
-						entry.put(StaticFlowEntryPusher.COLUMN_INSTR_EXPERIMENTER, InstructionUtils.experimenterToString(((OFInstructionExperimenter) inst), log));
-						break;
-					default:
-						log.error("Could not decode OF1.1+ instruction type {}", inst); 
-					}
-				}
-			}	
-		}		
-
-		Match match = fm.getMatch();
-		// it's a shame we can't use the MatchUtils for this. It's kind of the same thing but storing in a different place.
-		Iterator<MatchField<?>> itr = match.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded)
-		while(itr.hasNext()) {
-			@SuppressWarnings("rawtypes") // this is okay here
-			MatchField mf = itr.next();
-			switch (mf.id) {
-			case IN_PORT: // iterates over only exact/masked fields. No need to check for null entries.
-				if (match.supports(MatchField.IN_PORT) && match.isExact(MatchField.IN_PORT)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, match.get(MatchField.IN_PORT).toString());
-				} else if (match.supportsMasked(MatchField.IN_PORT) && match.isPartiallyMasked(MatchField.IN_PORT)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, match.getMasked(MatchField.IN_PORT).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_IN_PORT, match.getVersion().toString());
-				}
-				break;
-			case ETH_SRC:
-				if (match.supports(MatchField.ETH_SRC) && match.isExact(MatchField.ETH_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, match.get(MatchField.ETH_SRC).toString());
-				} else if (match.supportsMasked(MatchField.ETH_SRC) && match.isPartiallyMasked(MatchField.ETH_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, match.getMasked(MatchField.ETH_SRC).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_DL_SRC, match.getVersion().toString());
-				}
-				break;
-			case ETH_DST:
-				if (match.supports(MatchField.ETH_DST) && match.isExact(MatchField.ETH_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, match.get(MatchField.ETH_DST).toString());
-				} else if (match.supportsMasked(MatchField.ETH_DST) && match.isPartiallyMasked(MatchField.ETH_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, match.getMasked(MatchField.ETH_DST).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_DL_DST, match.getVersion().toString());
-				}
-				break;
-			case VLAN_VID:
-				if (match.supports(MatchField.VLAN_VID) && match.isExact(MatchField.VLAN_VID)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, match.get(MatchField.VLAN_VID).toString());
-				} else if (match.supportsMasked(MatchField.VLAN_VID) && match.isPartiallyMasked(MatchField.VLAN_VID)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, match.getMasked(MatchField.VLAN_VID).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_DL_VLAN, match.getVersion().toString());
-				}
-				break;
-			case VLAN_PCP:
-				if (match.supports(MatchField.VLAN_PCP) && match.isExact(MatchField.VLAN_PCP)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, match.get(MatchField.VLAN_PCP).toString());
-				} else if (match.supportsMasked(MatchField.VLAN_PCP) && match.isPartiallyMasked(MatchField.VLAN_PCP)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, match.getMasked(MatchField.VLAN_PCP).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, match.getVersion().toString());
-				}
-				break;
-			case ETH_TYPE:
-				if (match.supports(MatchField.ETH_TYPE) && match.isExact(MatchField.ETH_TYPE)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, match.get(MatchField.ETH_TYPE).toString());
-				} else if (match.supportsMasked(MatchField.ETH_TYPE) && match.isPartiallyMasked(MatchField.ETH_TYPE)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, match.getMasked(MatchField.ETH_TYPE).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_DL_TYPE, match.getVersion().toString());
-				}
-				break;
-			case IP_ECN: // TOS = [DSCP bits 0-5] + [ECN bits 6-7] --> bitwise OR to get TOS byte (have separate columns now though)
-				if (match.supports(MatchField.IP_ECN) && match.isExact(MatchField.IP_ECN)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW_ECN, match.get(MatchField.IP_ECN).toString());
-				} else if (match.supportsMasked(MatchField.IP_ECN) && match.isPartiallyMasked(MatchField.IP_ECN)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW_ECN, match.getMasked(MatchField.IP_ECN).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_NW_ECN, match.getVersion().toString());
-				}
-				break;
-			case IP_DSCP: // Even for OF1.0, loxi will break ECN and DSCP up from the API's POV. This method is only invoked by a SFP service push from another module
-				if (match.supports(MatchField.IP_DSCP) && match.isExact(MatchField.IP_DSCP)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW_DSCP, match.get(MatchField.IP_DSCP).toString());
-				} else if (match.supportsMasked(MatchField.IP_DSCP) && match.isPartiallyMasked(MatchField.IP_DSCP)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW_DSCP, match.getMasked(MatchField.IP_DSCP).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_NW_DSCP, match.getVersion().toString());
-				}
-				break;
-			case IP_PROTO:
-				if (match.supports(MatchField.IP_PROTO) && match.isExact(MatchField.IP_PROTO)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, match.get(MatchField.IP_PROTO).toString());
-				} else if (match.supportsMasked(MatchField.IP_PROTO) && match.isPartiallyMasked(MatchField.IP_PROTO)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, match.getMasked(MatchField.IP_PROTO).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_NW_PROTO, match.getVersion().toString());
-				}
-				break;
-			case IPV4_SRC:
-				if (match.supports(MatchField.IPV4_SRC) && match.isExact(MatchField.IPV4_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, match.get(MatchField.IPV4_SRC).toString());
-				} else if (match.supportsMasked(MatchField.IPV4_SRC) && match.isPartiallyMasked(MatchField.IPV4_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, match.getMasked(MatchField.IPV4_SRC).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_NW_SRC, match.getVersion().toString());
-				}
-				break;
-			case IPV4_DST:
-				if (match.supports(MatchField.IPV4_DST) && match.isExact(MatchField.IPV4_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, match.get(MatchField.IPV4_DST).toString());
-				} else if (match.supportsMasked(MatchField.IPV4_DST) && match.isPartiallyMasked(MatchField.IPV4_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, match.getMasked(MatchField.IPV4_DST).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_NW_DST, match.getVersion().toString());
-				}
-				break;
-			case TCP_SRC:
-				if (match.supports(MatchField.TCP_SRC) && match.isExact(MatchField.TCP_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_TCP_SRC, match.get(MatchField.TCP_SRC).toString());
-				} else if (match.supportsMasked(MatchField.TCP_SRC) && match.isPartiallyMasked(MatchField.TCP_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_TCP_SRC, match.getMasked(MatchField.TCP_SRC).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_TCP_SRC, match.getVersion().toString());
-				}
-				break;
-			case UDP_SRC:
-				if (match.supports(MatchField.UDP_SRC) && match.isExact(MatchField.UDP_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_UDP_SRC, match.get(MatchField.UDP_SRC).toString());
-				} else if (match.supportsMasked(MatchField.UDP_SRC) && match.isPartiallyMasked(MatchField.UDP_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_UDP_SRC, match.getMasked(MatchField.UDP_SRC).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_UDP_SRC, match.getVersion().toString());
-				}
-				break;
-			case SCTP_SRC:
-				if (match.supports(MatchField.SCTP_SRC) && match.isExact(MatchField.SCTP_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.get(MatchField.SCTP_SRC).toString());
-				} else if (match.supportsMasked(MatchField.SCTP_SRC) && match.isPartiallyMasked(MatchField.SCTP_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.getMasked(MatchField.SCTP_SRC).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.getVersion().toString());
-				}
-				break;
-			case TCP_DST:
-				if (match.supports(MatchField.TCP_DST) && match.isExact(MatchField.TCP_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_TCP_DST, match.get(MatchField.TCP_DST).toString());
-				} else if (match.supportsMasked(MatchField.TCP_DST) && match.isPartiallyMasked(MatchField.TCP_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_TCP_DST, match.getMasked(MatchField.TCP_DST).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_TCP_DST, match.getVersion().toString());
-				}
-				break;
-			case UDP_DST:
-				if (match.supports(MatchField.UDP_DST) && match.isExact(MatchField.UDP_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_UDP_DST, match.get(MatchField.UDP_DST).toString());
-				} else if (match.supportsMasked(MatchField.UDP_DST) && match.isPartiallyMasked(MatchField.UDP_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_UDP_DST, match.getMasked(MatchField.UDP_DST).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_UDP_DST, match.getVersion().toString());
-				}
-				break;
-			case SCTP_DST:
-				if (match.supports(MatchField.SCTP_DST) && match.isExact(MatchField.SCTP_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_SCTP_DST, match.get(MatchField.SCTP_DST).toString());
-				} else if (match.supportsMasked(MatchField.SCTP_DST) && match.isPartiallyMasked(MatchField.SCTP_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_SCTP_DST, match.getMasked(MatchField.SCTP_DST).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_SCTP_DST, match.getVersion().toString());
-				}
-				break;
-			case ICMPV4_TYPE:
-				if (match.supports(MatchField.ICMPV4_TYPE) && match.isExact(MatchField.ICMPV4_TYPE)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.get(MatchField.ICMPV4_TYPE).toString());
-				} else if (match.supportsMasked(MatchField.ICMPV4_TYPE) && match.isPartiallyMasked(MatchField.ICMPV4_TYPE)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.getMasked(MatchField.ICMPV4_TYPE).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.getVersion().toString());
-				}
-				break;
-			case ICMPV4_CODE:
-				if (match.supports(MatchField.ICMPV4_CODE) && match.isExact(MatchField.ICMPV4_CODE)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.get(MatchField.ICMPV4_CODE).toString());
-				} else if (match.supportsMasked(MatchField.ICMPV4_CODE) && match.isPartiallyMasked(MatchField.ICMPV4_CODE)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.getMasked(MatchField.ICMPV4_CODE).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.getVersion().toString());
-				}
-				break;
-			case ARP_OP:
-				if (match.supports(MatchField.ARP_OP) && match.isExact(MatchField.ARP_OP)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.get(MatchField.ARP_OP).toString());
-				} else if (match.supportsMasked(MatchField.ARP_OP) && match.isPartiallyMasked(MatchField.ARP_OP)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.getMasked(MatchField.ARP_OP).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.getVersion().toString());
-				}
-				break;
-			case ARP_SHA:
-				if (match.supports(MatchField.ARP_SHA) && match.isExact(MatchField.ARP_SHA)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, match.get(MatchField.ARP_SHA).toString());
-				} else if (match.supportsMasked(MatchField.ARP_SHA) && match.isPartiallyMasked(MatchField.ARP_SHA)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, match.getMasked(MatchField.ARP_SHA).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ARP_SHA, match.getVersion().toString());
-				}
-				break;
-			case ARP_THA:
-				if (match.supports(MatchField.ARP_THA) && match.isExact(MatchField.ARP_THA)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, match.get(MatchField.ARP_THA).toString());
-				} else if (match.supportsMasked(MatchField.ARP_THA) && match.isPartiallyMasked(MatchField.ARP_THA)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, match.getMasked(MatchField.ARP_THA).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ARP_DHA, match.getVersion().toString());
-				}
-				break;
-			case ARP_SPA:
-				if (match.supports(MatchField.ARP_SPA) && match.isExact(MatchField.ARP_SPA)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, match.get(MatchField.ARP_SPA).toString());
-				} else if (match.supportsMasked(MatchField.ARP_SPA) && match.isPartiallyMasked(MatchField.ARP_SPA)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, match.getMasked(MatchField.ARP_SPA).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ARP_SPA, match.getVersion().toString());
-				}
-				break;
-			case ARP_TPA:
-				if (match.supports(MatchField.ARP_TPA) && match.isExact(MatchField.ARP_TPA)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, match.get(MatchField.ARP_TPA).toString());
-				} else if (match.supportsMasked(MatchField.ARP_TPA) && match.isPartiallyMasked(MatchField.ARP_TPA)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, match.getMasked(MatchField.ARP_TPA).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ARP_DPA, match.getVersion().toString());
-				}
-				break;
-			case IPV6_SRC:				
-				if (match.supports(MatchField.IPV6_SRC) && match.isExact(MatchField.IPV6_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW6_SRC, match.get(MatchField.IPV6_SRC).toString());
-				} else if (match.supportsMasked(MatchField.IPV6_SRC) && match.isPartiallyMasked(MatchField.IPV6_SRC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW6_SRC, match.getMasked(MatchField.IPV6_SRC).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_NW6_SRC, match.getVersion().toString());
-				}
-				break;
-			case IPV6_DST:			
-				if (match.supports(MatchField.IPV6_DST) && match.isExact(MatchField.IPV6_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW6_DST, match.get(MatchField.IPV6_DST).toString());
-				} else if (match.supportsMasked(MatchField.IPV6_DST) && match.isPartiallyMasked(MatchField.IPV6_DST)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_NW6_DST, match.getMasked(MatchField.IPV6_DST).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_NW6_DST, match.getVersion().toString());
-				}
-				break;	
-			case IPV6_FLABEL:			
-				if (match.supports(MatchField.IPV6_FLABEL) && match.isExact(MatchField.IPV6_FLABEL)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.get(MatchField.IPV6_FLABEL).toString());
-				} else if (match.supportsMasked(MatchField.IPV6_FLABEL) && match.isPartiallyMasked(MatchField.IPV6_FLABEL)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.getMasked(MatchField.IPV6_FLABEL).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.getVersion().toString());
-				}
-				break;	
-			case ICMPV6_TYPE:				
-				if (match.supports(MatchField.ICMPV6_TYPE) && match.isExact(MatchField.ICMPV6_TYPE)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, match.get(MatchField.ICMPV6_TYPE).toString());
-				} else if (match.supportsMasked(MatchField.ICMPV6_TYPE) && match.isPartiallyMasked(MatchField.ICMPV6_TYPE)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, match.getMasked(MatchField.ICMPV6_TYPE).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, match.getVersion().toString());
-				}
-				break;
-			case ICMPV6_CODE:				
-				if (match.supports(MatchField.ICMPV6_CODE) && match.isExact(MatchField.ICMPV6_CODE)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.get(MatchField.ICMPV6_CODE).toString());
-				} else if (match.supportsMasked(MatchField.ICMPV6_CODE) && match.isPartiallyMasked(MatchField.ICMPV6_CODE)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.getMasked(MatchField.ICMPV6_CODE).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.getVersion().toString());
-				}
-				break;
-			case IPV6_ND_SLL:			
-				if (match.supports(MatchField.IPV6_ND_SLL) && match.isExact(MatchField.IPV6_ND_SLL)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ND_SLL, match.get(MatchField.IPV6_ND_SLL).toString());
-				} else if (match.supportsMasked(MatchField.IPV6_ND_SLL) && match.isPartiallyMasked(MatchField.IPV6_ND_SLL)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ND_SLL, match.getMasked(MatchField.IPV6_ND_SLL).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ND_SLL, match.getVersion().toString());
-				}
-				break;	
-			case IPV6_ND_TLL:				
-				if (match.supports(MatchField.IPV6_ND_TLL) && match.isExact(MatchField.IPV6_ND_TLL)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ND_TLL, match.get(MatchField.IPV6_ND_TLL).toString());
-				} else if (match.supportsMasked(MatchField.IPV6_ND_TLL) && match.isPartiallyMasked(MatchField.IPV6_ND_TLL)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ND_TLL, match.getMasked(MatchField.IPV6_ND_TLL).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ND_TLL, match.getVersion().toString());
-				}
-				break;	
-			case IPV6_ND_TARGET:				
-				if (match.supports(MatchField.IPV6_ND_TARGET) && match.isExact(MatchField.IPV6_ND_TARGET)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ND_TARGET, match.get(MatchField.IPV6_ND_TARGET).toString());
-				} else if (match.supportsMasked(MatchField.IPV6_ND_TARGET) && match.isPartiallyMasked(MatchField.IPV6_ND_TARGET)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_ND_TARGET, match.getMasked(MatchField.IPV6_ND_TARGET).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_ND_TARGET, match.getVersion().toString());
-				}
-				break;					
-			case MPLS_LABEL:
-				if (match.supports(MatchField.MPLS_LABEL) && match.isExact(MatchField.MPLS_LABEL)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.get(MatchField.MPLS_LABEL).toString());
-				} else if (match.supportsMasked(MatchField.MPLS_LABEL) && match.isPartiallyMasked(MatchField.MPLS_LABEL)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.getMasked(MatchField.MPLS_LABEL).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.getVersion().toString());
-				}
-				break;
-			case MPLS_TC:
-				if (match.supports(MatchField.MPLS_TC) && match.isExact(MatchField.MPLS_TC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, match.get(MatchField.MPLS_TC).toString());
-				} else if (match.supportsMasked(MatchField.MPLS_TC) && match.isPartiallyMasked(MatchField.MPLS_TC)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, match.getMasked(MatchField.MPLS_TC).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_MPLS_TC, match.getVersion().toString());
-				}
-				break;
-			case MPLS_BOS:
-				if (match.supports(MatchField.MPLS_BOS) && match.isExact(MatchField.MPLS_BOS)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.get(MatchField.MPLS_BOS).toString());
-				} else if (match.supportsMasked(MatchField.MPLS_BOS) && match.isPartiallyMasked(MatchField.MPLS_BOS)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.getMasked(MatchField.MPLS_BOS).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.getVersion().toString());
-				}
-				break;			
-			case METADATA:
-				if (match.supports(MatchField.METADATA) && match.isExact(MatchField.METADATA)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_METADATA, match.get(MatchField.METADATA).toString());
-				} else if (match.supportsMasked(MatchField.METADATA) && match.isPartiallyMasked(MatchField.METADATA)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_METADATA, match.getMasked(MatchField.METADATA).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_METADATA, match.getVersion().toString());
-				}
-				break;
-			case TUNNEL_ID:
-				if (match.supports(MatchField.TUNNEL_ID) && match.isExact(MatchField.TUNNEL_ID)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.get(MatchField.TUNNEL_ID).toString());
-				} else if (match.supportsMasked(MatchField.TUNNEL_ID) && match.isPartiallyMasked(MatchField.TUNNEL_ID)) {
-					entry.put(StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.getMasked(MatchField.TUNNEL_ID).toString());
-				} else {
-					log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", 
-							StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.getVersion().toString());
-				}
-				break;	
-			// case PBB_ISID not implemented in loxi
-			default:
-				log.error("Unhandled Match when parsing OFFlowMod: {}, {}", mf, mf.id);
-				break;
-			} // end switch-case
-		} // end while
-				
-		int result = StaticFlowEntryPusherResource.checkActions(entry);
-		if (result == -1)
-			throw new Exception("Invalid action/instructions");
-		
-		return entry;
-	}
-
-	/**
-	 * Turns a JSON formatted Static Flow Pusher string into a storage entry
-	 * Expects a string in JSON along the lines of:
-	 *        {
-	 *            "switch":       "AA:BB:CC:DD:EE:FF:00:11",
-	 *            "name":         "flow-mod-1",
-	 *            "cookie":       "0",
-	 *            "priority":     "32768",
-	 *            "ingress-port": "1",
-	 *            "actions":      "output=2",
-	 *        }
-	 * @param fmJson The JSON formatted static flow pusher entry
-	 * @return The map of the storage entry
-	 * @throws IOException If there was an error parsing the JSON
-	 */
-	public static Map<String, Object> jsonToStorageEntry(String fmJson) throws IOException {
-		Map<String, Object> entry = new HashMap<String, Object>();
-		MappingJsonFactory f = new MappingJsonFactory();
-		JsonParser jp;
-		
-		String tpSrcPort = "NOT_SPECIFIED";
-		String tpDstPort = "NOT_SPECIFIED";
-		String ipProto = "NOT_SPECIFIED";
-
-		try {
-			jp = f.createParser(fmJson);
-		} catch (JsonParseException e) {
-			throw new IOException(e);
-		}
-
-		jp.nextToken();
-		if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
-			throw new IOException("Expected START_OBJECT");
-		}
-
-		while (jp.nextToken() != JsonToken.END_OBJECT) {
-			if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
-				throw new IOException("Expected FIELD_NAME");
-			}
-
-			String n = jp.getCurrentName();
-			jp.nextToken();
-
-			// Java 7 switch-case on strings automatically checks for (deep) string equality.
-			// IMHO, this makes things easier on the eyes than if, else if, else's, and it
-			// seems to be more efficient than walking through a long list of if-else-ifs
-
-			// A simplification is to make the column names the same strings as those used to
-			// compose the JSON flow entry; keeps all names/keys centralized and reduces liklihood
-			// for future string errors.
-			switch (n) {
-			case StaticFlowEntryPusher.COLUMN_NAME:
-				entry.put(StaticFlowEntryPusher.COLUMN_NAME, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_SWITCH:
-				entry.put(StaticFlowEntryPusher.COLUMN_SWITCH, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_TABLE_ID:
-				entry.put(StaticFlowEntryPusher.COLUMN_TABLE_ID, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ACTIVE:
-				entry.put(StaticFlowEntryPusher.COLUMN_ACTIVE, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_IDLE_TIMEOUT:
-				entry.put(StaticFlowEntryPusher.COLUMN_IDLE_TIMEOUT, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_HARD_TIMEOUT:
-				entry.put(StaticFlowEntryPusher.COLUMN_HARD_TIMEOUT, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_PRIORITY:
-				entry.put(StaticFlowEntryPusher.COLUMN_PRIORITY, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_COOKIE: // set manually, or computed from name
-				entry.put(StaticFlowEntryPusher.COLUMN_COOKIE, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_IN_PORT:
-				entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_DL_SRC:
-				entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_DL_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_DL_VLAN:
-				entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP:
-				entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_DL_TYPE:
-				entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_NW_TOS: // only valid for OF1.0; all other should specify specifics (ECN and/or DSCP bits)
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_TOS, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_NW_ECN:
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_ECN, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_NW_DSCP:
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_DSCP, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_NW_PROTO:
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, jp.getText());
-				ipProto = jp.getText();
-				break;
-			case StaticFlowEntryPusher.COLUMN_NW_SRC:
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_NW_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_SCTP_SRC:
-				entry.put(StaticFlowEntryPusher.COLUMN_SCTP_SRC, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_SCTP_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_SCTP_DST, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_UDP_SRC:
-				entry.put(StaticFlowEntryPusher.COLUMN_UDP_SRC, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_UDP_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_UDP_DST, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_TCP_SRC:
-				entry.put(StaticFlowEntryPusher.COLUMN_TCP_SRC, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_TCP_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_TCP_DST, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_TP_SRC: // support for OF1.0 generic transport ports
-				entry.put(StaticFlowEntryPusher.COLUMN_TP_SRC, jp.getText());
-				tpSrcPort = jp.getText();
-				break;
-			case StaticFlowEntryPusher.COLUMN_TP_DST:
-				entry.put(StaticFlowEntryPusher.COLUMN_TP_DST, jp.getText());
-				tpDstPort = jp.getText();
-				break;
-			case StaticFlowEntryPusher.COLUMN_ICMP_TYPE:
-				entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ICMP_CODE:
-				entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ARP_OPCODE:
-				entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ARP_SHA:
-				entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ARP_DHA:
-				entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ARP_SPA:
-				entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ARP_DPA:
-				entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, jp.getText());
-				break;		
-			case StaticFlowEntryPusher.COLUMN_NW6_SRC:				
-				entry.put(StaticFlowEntryPusher.COLUMN_NW6_SRC, jp.getText());
-				break;	
-			case StaticFlowEntryPusher.COLUMN_NW6_DST:				
-				entry.put(StaticFlowEntryPusher.COLUMN_NW6_DST, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL:								
-				entry.put(StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, jp.getText());
-				break;	
-			case StaticFlowEntryPusher.COLUMN_ICMP6_TYPE:				
-				entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ICMP6_CODE:						
-				entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_CODE, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ND_SLL:				
-				entry.put(StaticFlowEntryPusher.COLUMN_ND_SLL, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ND_TLL:			
-				entry.put(StaticFlowEntryPusher.COLUMN_ND_TLL, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ND_TARGET:					
-				entry.put(StaticFlowEntryPusher.COLUMN_ND_TARGET, jp.getText());
-				break;				
-			case StaticFlowEntryPusher.COLUMN_MPLS_LABEL:
-				entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_MPLS_TC:
-				entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_MPLS_BOS:
-				entry.put(StaticFlowEntryPusher.COLUMN_MPLS_BOS, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_METADATA:
-				entry.put(StaticFlowEntryPusher.COLUMN_METADATA, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_TUNNEL_ID:
-				entry.put(StaticFlowEntryPusher.COLUMN_TUNNEL_ID, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_PBB_ISID: // not supported as match in loxi right now
-				entry.put(StaticFlowEntryPusher.COLUMN_PBB_ISID, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_ACTIONS:
-				entry.put(StaticFlowEntryPusher.COLUMN_ACTIONS, jp.getText());
-				break;
-				
-			/* 
-			 * All OF1.1+ instructions.
-			 */
-			case StaticFlowEntryPusher.COLUMN_INSTR_APPLY_ACTIONS:
-				entry.put(StaticFlowEntryPusher.COLUMN_INSTR_APPLY_ACTIONS, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_INSTR_WRITE_ACTIONS:
-				entry.put(StaticFlowEntryPusher.COLUMN_INSTR_WRITE_ACTIONS, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_INSTR_CLEAR_ACTIONS:
-				entry.put(StaticFlowEntryPusher.COLUMN_INSTR_CLEAR_ACTIONS, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_INSTR_GOTO_METER:
-				entry.put(StaticFlowEntryPusher.COLUMN_INSTR_GOTO_METER, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_INSTR_GOTO_TABLE:
-				entry.put(StaticFlowEntryPusher.COLUMN_INSTR_GOTO_TABLE, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_INSTR_WRITE_METADATA:
-				entry.put(StaticFlowEntryPusher.COLUMN_INSTR_WRITE_METADATA, jp.getText());
-				break;
-			case StaticFlowEntryPusher.COLUMN_INSTR_EXPERIMENTER:
-				entry.put(StaticFlowEntryPusher.COLUMN_INSTR_EXPERIMENTER, jp.getText());
-				break;
-			default:
-				log.error("Could not decode field from JSON string: {}", n);
-				break;
-			}  
-		} 
-		
-		// For OF1.0, transport ports are specified using generic tp_src, tp_dst type strings.
-		// Once the whole json string has been parsed, find out the IpProto to properly assign the ports.
-		// If IpProto not specified, print error, and make sure all TP columns are clear.
-		if (ipProto.equalsIgnoreCase("tcp")) {
-			if (!tpSrcPort.equals("NOT_SPECIFIED")) {
-				entry.remove(StaticFlowEntryPusher.COLUMN_TP_SRC);
-				entry.put(StaticFlowEntryPusher.COLUMN_TCP_SRC, tpSrcPort);
-			}
-			if (!tpDstPort.equals("NOT_SPECIFIED")) {
-				entry.remove(StaticFlowEntryPusher.COLUMN_TP_DST);
-				entry.put(StaticFlowEntryPusher.COLUMN_TCP_DST, tpDstPort);
-			}
-		} else if (ipProto.equalsIgnoreCase("udp")) {
-			if (!tpSrcPort.equals("NOT_SPECIFIED")) {
-				entry.remove(StaticFlowEntryPusher.COLUMN_TP_SRC);
-				entry.put(StaticFlowEntryPusher.COLUMN_UDP_SRC, tpSrcPort);
-			}
-			if (!tpDstPort.equals("NOT_SPECIFIED")) {
-				entry.remove(StaticFlowEntryPusher.COLUMN_TP_DST);
-				entry.put(StaticFlowEntryPusher.COLUMN_UDP_DST, tpDstPort);
-			}
-		} else if (ipProto.equalsIgnoreCase("sctp")) {
-			if (!tpSrcPort.equals("NOT_SPECIFIED")) {
-				entry.remove(StaticFlowEntryPusher.COLUMN_TP_SRC);
-				entry.put(StaticFlowEntryPusher.COLUMN_SCTP_SRC, tpSrcPort);
-			}
-			if (!tpDstPort.equals("NOT_SPECIFIED")) {
-				entry.remove(StaticFlowEntryPusher.COLUMN_TP_DST);
-				entry.put(StaticFlowEntryPusher.COLUMN_SCTP_DST, tpDstPort);
-			}
-		} else {
-			log.debug("Got IP protocol of '{}' and tp-src of '{}' and tp-dst of '" + tpDstPort + "' via SFP REST API", ipProto, tpSrcPort);
-		}
-
-		return entry;
-	}   
-}
-
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/OFFlowModMap.java b/src/main/java/net/floodlightcontroller/staticflowentry/web/OFFlowModMap.java
deleted file mode 100644
index f91fdedc36e0c5cefe68955e2ad27602df91cbdf..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/staticflowentry/web/OFFlowModMap.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package net.floodlightcontroller.staticflowentry.web;
-
-import java.util.Map;
-
-import org.projectfloodlight.openflow.protocol.OFFlowMod;
-
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
-@JsonSerialize(using=OFFlowModMapSerializer.class) 
-public class OFFlowModMap {
-
-	/*
-	 * Contains the following double-mapping:
-	 * Map<Switch-DPID-Str, Map<Flow-Name-Str, OFFlowMod>>
-	 */
-	private Map<String, Map<String, OFFlowMod>> theMap;
-	
-	public OFFlowModMap (Map<String, Map<String, OFFlowMod>> theMap) {
-		this.theMap = theMap;
-	}
-	
-	public Map<String, Map<String, OFFlowMod>> getMap() {
-		return theMap;
-	}
-}
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryDeleteResource.java b/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryDeleteResource.java
deleted file mode 100644
index d64b97103c24fdcfa385adf0f52195426a3632db..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryDeleteResource.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
-*    Copyright 2011, Big Switch Networks, Inc.
-*    Originally created by David Erickson, Stanford University
-*
-*    Licensed under the Apache License, Version 2.0 (the "License"); you may
-*    not use this file except in compliance with the License. You may obtain
-*    a copy of the License at
-*
-*         http://www.apache.org/licenses/LICENSE-2.0
-*
-*    Unless required by applicable law or agreed to in writing, software
-*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-*    License for the specific language governing permissions and limitations
-*    under the License.
-**/
-
-package net.floodlightcontroller.staticflowentry.web;
-
-import java.io.IOException;
-
-import org.restlet.resource.Post;
-import org.restlet.resource.ServerResource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.floodlightcontroller.staticflowentry.StaticFlowEntries;
-import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
-import net.floodlightcontroller.storage.IStorageSourceService;
-
-/**
- * Deletes a static flow entry to the storage source
- * @author alexreimers
- * 
- * @author Henrique Rodrigues <hsr@cs.ucsd.edu>
- * 		Contributed with splitting StaticFlowEntryPusherResource into 
- * 		two, to make Floodlight's StaticFlowEntryPusher restlet resource
- * 		REST compliant.
- * 
- */
-public class StaticFlowEntryDeleteResource extends ServerResource {
-    protected static Logger log = LoggerFactory.getLogger(StaticFlowEntryDeleteResource.class);
-
-    @Post
-    public String del(String fmJson) {
-        IStorageSourceService storageSource =
-                (IStorageSourceService)getContext().getAttributes().
-                    get(IStorageSourceService.class.getCanonicalName());
-        String fmName = null;
-        if (fmJson == null) {
-            return "{\"status\" : \"Error! No data posted.\"}";
-        }
-        try {
-            fmName = StaticFlowEntries.getEntryNameFromJson(fmJson);
-            if (fmName == null) {
-                return "{\"status\" : \"Error deleting entry, no name provided\"}";
-            }
-        } catch (IOException e) {
-            log.error("Error deleting flow mod request: " + fmJson, e);
-            return "{\"status\" : \"Error deleting entry, see log for details\"}";
-        }
-
-        storageSource.deleteRowAsync(StaticFlowEntryPusher.TABLE_NAME, fmName);
-        return "{\"status\" : \"Entry " + fmName + " deleted\"}";
-    }
-}
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryWebRoutable.java b/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryWebRoutable.java
deleted file mode 100644
index 9bc1497cedcece542f30bb762a1ed8b51879ae81..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryWebRoutable.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- *    Copyright 2013, Big Switch Networks, Inc.
- *
- *    Licensed under the Apache License, Version 2.0 (the "License"); you may
- *    not use this file except in compliance with the License. You may obtain
- *    a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- *    License for the specific language governing permissions and limitations
- *    under the License.
- **/
-
-package net.floodlightcontroller.staticflowentry.web;
-
-import net.floodlightcontroller.restserver.RestletRoutable;
-
-import org.restlet.Context;
-import org.restlet.Restlet;
-import org.restlet.routing.Router;
-
-public class StaticFlowEntryWebRoutable implements RestletRoutable {
-    /**
-     * Create the Restlet router and bind to the proper resources.
-     */
-    @Override
-    public Restlet getRestlet(Context context) {
-        Router router = new Router(context);
-        router.attach("/json", StaticFlowEntryPusherResource.class);
-        router.attach("/json/store", StaticFlowEntryPusherResource.class);
-        router.attach("/json/delete", StaticFlowEntryDeleteResource.class);
-        router.attach("/clear/{switch}/json", ClearStaticFlowEntriesResource.class);
-        router.attach("/list/{switch}/json", ListStaticFlowEntriesResource.class);
-        return router;
-    }
-
-    /**
-     * Set the base path for the Topology
-     */
-    @Override
-    public String basePath() {
-        return "/wm/staticflowpusher";
-    }
-}
diff --git a/src/main/java/net/floodlightcontroller/statistics/web/BandwidthResource.java b/src/main/java/net/floodlightcontroller/statistics/web/BandwidthResource.java
index a09e5f9e0d4d49fce8467f7825f0310eaa981b52..55b2e619a7b2e4d5d09e9881585dd5838577133f 100644
--- a/src/main/java/net/floodlightcontroller/statistics/web/BandwidthResource.java
+++ b/src/main/java/net/floodlightcontroller/statistics/web/BandwidthResource.java
@@ -17,59 +17,63 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class BandwidthResource extends ServerResource {
-	private static final Logger log = LoggerFactory.getLogger(BandwidthResource.class);
+    private static final Logger log = LoggerFactory.getLogger(BandwidthResource.class);
 
-	@Get("json")
-	public Object retrieve() {
-		IStatisticsService statisticsService = (IStatisticsService) getContext().getAttributes().get(IStatisticsService.class.getCanonicalName());
-		IOFSwitchService switchService = (IOFSwitchService) getContext().getAttributes().get(IOFSwitchService.class.getCanonicalName());
+    @Get("json")
+    public Object retrieve() {
+        IStatisticsService statisticsService = (IStatisticsService) getContext().getAttributes().get(IStatisticsService.class.getCanonicalName());
+        IOFSwitchService switchService = (IOFSwitchService) getContext().getAttributes().get(IOFSwitchService.class.getCanonicalName());
 
-		String d = (String) getRequestAttributes().get(SwitchStatisticsWebRoutable.DPID_STR);
-		String p = (String) getRequestAttributes().get(SwitchStatisticsWebRoutable.PORT_STR);
+        String d = (String) getRequestAttributes().get(SwitchStatisticsWebRoutable.DPID_STR);
+        String p = (String) getRequestAttributes().get(SwitchStatisticsWebRoutable.PORT_STR);
 
-		DatapathId dpid = DatapathId.NONE;
+        DatapathId dpid = DatapathId.NONE;
 
-		if (!d.trim().equalsIgnoreCase("all")) {
-			try {
-				dpid = DatapathId.of(d);
-			} catch (Exception e) {
-				log.error("Could not parse DPID {}", d);
-				return Collections.singletonMap("ERROR", "Could not parse DPID" + d);
-			}
-		} /* else assume it's all */
+        if (!d.trim().equalsIgnoreCase("all")) {
+            try {
+                dpid = DatapathId.of(d);
+            } catch (Exception e) {
+                log.error("Could not parse DPID {}", d);
+                return Collections.singletonMap("ERROR", "Could not parse DPID " + d);
+            }
+        } /* else assume it's all */
 
-		OFPort port = OFPort.ALL;
-		if (!p.trim().equalsIgnoreCase("all")) {
-			try {
-				port = OFPort.of(Integer.parseInt(p));
-			} catch (Exception e) {
-				log.error("Could not parse port {}", p);
-				return Collections.singletonMap("ERROR", "Could not parse port" + p);
-			}
-		}
+        OFPort port = OFPort.ALL;
+        if (!p.trim().equalsIgnoreCase("all")) {
+            try {
+                port = OFPort.of(Integer.parseInt(p));
+            } catch (Exception e) {
+                log.error("Could not parse port {}", p);
+                return Collections.singletonMap("ERROR", "Could not parse port " + p);
+            }
+        }
 
-		Set<SwitchPortBandwidth> spbs;
-		if (dpid.equals(DatapathId.NONE)) { /* do all DPIDs */
-			if (port.equals(OFPort.ALL)) { /* do all ports */
-				spbs = new HashSet<SwitchPortBandwidth>(statisticsService.getBandwidthConsumption().values());
-			} else {
-				spbs = new HashSet<SwitchPortBandwidth>();
-				for (DatapathId id : switchService.getAllSwitchDpids()) {
-					SwitchPortBandwidth spb = statisticsService.getBandwidthConsumption(id, port);
-					if (spb != null) {
-						spbs.add(spb);
-					}
-				}
-			}
-		} else {
-			spbs = new HashSet<SwitchPortBandwidth>();
-			for (OFPortDesc pd : switchService.getSwitch(dpid).getPorts()) {
-				SwitchPortBandwidth spb = statisticsService.getBandwidthConsumption(dpid, pd.getPortNo());
-				if (spb != null) {
-					spbs.add(spb);
-				}
-			}
-		}
-		return spbs;
-	}
+        Set<SwitchPortBandwidth> spbs;
+        if (dpid.equals(DatapathId.NONE)) { /* do all DPIDs */
+            if (port.equals(OFPort.ALL)) { /* do all ports --> do all DPIDs; do all ports */
+                spbs = new HashSet<SwitchPortBandwidth>(statisticsService.getBandwidthConsumption().values());
+            } else {
+                spbs = new HashSet<SwitchPortBandwidth>();
+                for (DatapathId id : switchService.getAllSwitchDpids()) { /* do all DPIDs; do specific port */
+                    SwitchPortBandwidth spb = statisticsService.getBandwidthConsumption(id, port);
+                    if (spb != null) {
+                        spbs.add(spb);
+                    }
+                }
+            }
+        } else { /* do specific DPID */
+            if (!port.equals(OFPort.ALL)) { /* do specific port --> do specific DPID; do specific port */
+                spbs = new HashSet<SwitchPortBandwidth>(Collections.singleton(statisticsService.getBandwidthConsumption(dpid, port)));
+            } else {
+                spbs = new HashSet<SwitchPortBandwidth>();
+                for (OFPortDesc pd : switchService.getSwitch(dpid).getPorts()) { /* do specific DPID; do all ports */
+                    SwitchPortBandwidth spb = statisticsService.getBandwidthConsumption(dpid, pd.getPortNo());
+                    if (spb != null) {
+                        spbs.add(spb);
+                    }
+                }
+            }
+        }
+        return spbs;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/testmodule/TestModule.java b/src/main/java/net/floodlightcontroller/testmodule/TestModule.java
deleted file mode 100644
index 3122ce377b1eac6d3ba2307bb61d88c5cd44bb1e..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/testmodule/TestModule.java
+++ /dev/null
@@ -1,289 +0,0 @@
-package net.floodlightcontroller.testmodule;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Map;
-
-import org.projectfloodlight.openflow.protocol.OFFactory;
-import org.projectfloodlight.openflow.protocol.OFFlowMod;
-import org.projectfloodlight.openflow.protocol.OFPortDesc;
-import org.projectfloodlight.openflow.protocol.action.OFAction;
-import org.projectfloodlight.openflow.protocol.action.OFActionPushVlan;
-import org.projectfloodlight.openflow.protocol.action.OFActionSetField;
-import org.projectfloodlight.openflow.protocol.action.OFActions;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
-import org.projectfloodlight.openflow.protocol.instruction.OFInstructions;
-import org.projectfloodlight.openflow.protocol.match.Match;
-import org.projectfloodlight.openflow.protocol.match.MatchField;
-import org.projectfloodlight.openflow.protocol.oxm.OFOxms;
-import org.projectfloodlight.openflow.types.DatapathId;
-import org.projectfloodlight.openflow.types.EthType;
-import org.projectfloodlight.openflow.types.MacAddress;
-import org.projectfloodlight.openflow.types.OFPort;
-import org.projectfloodlight.openflow.types.OFVlanVidMatch;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.floodlightcontroller.core.IOFSwitchListener;
-import net.floodlightcontroller.core.PortChangeType;
-import net.floodlightcontroller.core.internal.IOFSwitchService;
-import net.floodlightcontroller.core.module.FloodlightModuleContext;
-import net.floodlightcontroller.core.module.FloodlightModuleException;
-import net.floodlightcontroller.core.module.IFloodlightModule;
-import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
-
-public class TestModule implements IFloodlightModule, IOFSwitchListener {
-
-	private static IStaticFlowEntryPusherService sfps;
-	private static IOFSwitchService switchService;
-	private static Logger log;
-	
-	@Override
-	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
-		return null;
-	}
-
-	@Override
-	public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
-		return null;
-	}
-
-	@Override
-	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
-		Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
-		l.add(IStaticFlowEntryPusherService.class);
-		l.add(IOFSwitchService.class);
-		return l;
-	}
-
-	@Override
-	public void init(FloodlightModuleContext context)
-			throws FloodlightModuleException {
-		sfps = context.getServiceImpl(IStaticFlowEntryPusherService.class);
-		switchService = context.getServiceImpl(IOFSwitchService.class);
-		switchService.addOFSwitchListener(this);
-		log = LoggerFactory.getLogger(TestModule.class);
-		if (sfps == null) {
-			log.error("Static Flow Pusher Service not found!");
-		}
-	}
-
-	@Override
-	public void startUp(FloodlightModuleContext context)
-			throws FloodlightModuleException {
-		
-
-	}
-
-	@Override
-	public void switchAdded(DatapathId switchId) {
-		OFFactory factory = switchService.getSwitch(switchId).getOFFactory();
-		
-		/*OFFlowAdd.Builder fab = factory.buildFlowAdd();
-		fab.setMatch(factory.buildMatch().setExact(MatchField.ETH_TYPE, EthType.IPv4)
-				.setMasked(MatchField.IPV4_SRC, IPv4Address.of("10.0.123.1"), IPv4Address.of("255.255.0.255"))
-				.build());
-		fab.setBufferId(OFBufferId.NO_BUFFER);
-		if (switchId.equals(DatapathId.of(1)))
-		switchService.getSwitch(switchId).write(fab.build());*/
-		
-		/*OFMeterStatsRequest req;
-		ListenableFuture<OFMeterStatsReply> reply = switchService.getActiveSwitch(switchId).writeRequest(req);
-		try {
-			for (OFMeterStats entry : reply.get().getEntries()) {
-				U64 byteInCount = entry.getByteInCount();
-				for (OFMeterBandStats mbs : entry.getBandStats()) {
-					U64 byteBandCount = mbs.getByteBandCount();
-					U64 pktBandCount = mbs.getPacketBandCount();
-				}
-			}
-		} catch (InterruptedException | ExecutionException e) {
-			e.printStackTrace();
-		}*/
-		
-		OFFactory f =factory;
-        Match.Builder mb =f.buildMatch();
-        mb.setExact(MatchField.ETH_SRC, MacAddress.of(2));
-        Match m=mb.build();
-        ArrayList<OFAction> actionList = new ArrayList<OFAction>();
-        OFActions actions = f.actions();
-        OFActionPushVlan vlan =actions.pushVlan(EthType.of(0x8100));
-        actionList.add(vlan);
-
-
-        OFOxms oxms =f.oxms();
-        OFActionSetField vlanid=actions.buildSetField().setField(oxms.buildVlanVid().setValue(OFVlanVidMatch.ofVlan(10)).build()).build();
-        actionList.add(vlanid);
-        OFInstructions inst=f.instructions(); 
-        OFInstructionApplyActions apply=inst.buildApplyActions().setActions(actionList).build();
-        ArrayList<OFInstruction> instList= new ArrayList<OFInstruction>();
-        instList.add(apply);
-        OFFlowMod.Builder fmb = factory.buildFlowAdd();
-        OFFlowMod msg = fmb.setPriority(32769)
-        .setMatch(m)
-        .setInstructions(instList)
-        .setOutPort(OFPort.of(1))
-        .build();
-
-        switchService.getSwitch(switchId).write(msg);
-		
-		
-		/*
-		 * An attempt at meters, but they aren't supported anywhere, yet... 
-		 * OFMeterBand mb = factory.meterBands().buildDrop()
-				.setRate(1000)
-				.setBurstSize(1000)
-				.build();
-		ArrayList<OFMeterBand> mbl = new ArrayList<OFMeterBand>();
-		mbl.add(mb);
-				
-		OFMeterMod mm = factory.buildMeterMod()
-				.setMeters(mbl)
-				.setMeterId(1)
-				.setCommand(OFMeterModCommandSerializerVer13.ADD_VAL) 
-				.build(); 
-		// This is a bug. You should be able to directly do OFMeterModCommand.ADD */
-		
-		/*HashSet<OFTableConfig> tblCfg = new HashSet<OFTableConfig>();
-		tblCfg.add(OFTableConfig.TABLE_MISS_CONTROLLER);
-		
-		ArrayList<OFTableModProp> tabModPropList = new ArrayList<OFTableModProp>();
-		OFTableModProp propEvic = switchService.getActiveSwitch(switchId).getOFFactory().tableDesc(TableId.ALL, arg1)
-		tabModPropList.add(propEvic);
-		OFTableMod tm = switchService.getActiveSwitch(switchId).getOFFactory().buildTableMod()
-				.setProperties(pro)
-		
-		switchService.getActiveSwitch(switchId).write(mm);*/
-		
-		/*OFFlowAdd.Builder fmb = factory.buildFlowAdd();
-		List<OFAction> actions = new ArrayList<OFAction>();
-        Match.Builder mb = factory.buildMatch();
-        List<OFInstruction> instructions = new ArrayList<OFInstruction>();
-        OFInstructionApplyActions.Builder applyActInstBldr = factory.instructions().buildApplyActions();
-        OFInstructionWriteActions.Builder writeActInstBldr = factory.instructions().buildWriteActions();
-        OFInstructionMeter.Builder mtrBldr = factory.instructions().buildMeter();
-        OFInstructionClearActions clrAct = factory.instructions().clearActions(); // no builder available (there's nothing to set anyway)
-        OFInstructionGotoTable.Builder gotoTblBldr = factory.instructions().buildGotoTable();
-        /*OFMeterBandDrop dropMeter = factory.meterBands().buildDrop().setBurstSize(100).setRate(200).build();
-        List<OFMeterBand> meterBandEntries = new ArrayList<OFMeterBand>();
-        OFMeterBandStats meterBandStats = factory.buildMeterBandStats().setPacketBandCount(U64.of(64)).setByteBandCount(U64.of(1024)).build();
-        meterBandEntries.add(meterBandStats);
-        OFMeterMod meterMod = factory.buildMeterMod().setCommand(OFMeterModCommand.ADD.ordinal()).setMeters(meterBandEntries).setMeterId(10).build();
-        factory.buildmeter*/
-        
-		/*try {
-			Thread.sleep(3000);
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		}*/
-        
-        // set a bunch of matches. Test for an OF1.0 and OF1.3 switch. See what happens if they are incorrectly applied.
-        /* L2 and ICMP TESTS  mb.setExact(MatchField.ETH_TYPE, EthType.IPv4);
-        mb.setExact(MatchField.ETH_SRC, MacAddress.BROADCAST);
-        mb.setExact(MatchField.ETH_DST, MacAddress.BROADCAST);
-        mb.setExact(MatchField.IPV4_SRC, IPv4Address.of("127.1.1.1"));
-        mb.setExact(MatchField.IPV4_DST, IPv4Address.of("128.2.2.2"));
-        mb.setExact(MatchField.IP_PROTO, IpProtocol.ICMP);
-        mb.setExact(MatchField.ICMPV4_CODE, ICMPv4Code.of((short)1));
-        mb.setExact(MatchField.ICMPV4_TYPE, ICMPv4Type.ECHO); 
-        OFActionOutput.Builder actionBuilder = factory.actions().buildOutput();
-        actions.add(factory.actions().output(OFPort.of(1), Integer.MAX_VALUE));
-        //actions.add(factory.actions().setField(factory.oxms().icmpv4Code(ICMPv4Code.of((short)1))));
-        //actions.add(factory.actions().setField(factory.oxms().icmpv4Type(ICMPv4Type.ALTERNATE_HOST_ADDRESS))); */
- 
-        
-        /* ARP TESTS  mb.setExact(MatchField.ETH_TYPE, EthType.ARP);
-        mb.setExact(MatchField.ARP_OP, ArpOpcode.REQUEST);
-        mb.setExact(MatchField.ARP_SHA, MacAddress.BROADCAST);
-        mb.setExact(MatchField.ARP_SPA, IPv4Address.of("130.127.39.241"));
-        mb.setExact(MatchField.ARP_THA, MacAddress.BROADCAST);
-        mb.setExact(MatchField.ARP_TPA, IPv4Address.of("130.127.39.241")); 
-        OFActionOutput.Builder actionBuilder = factory.actions().buildOutput();
-        actions.add(factory.actions().output(OFPort.LOCAL, Integer.MAX_VALUE));
-        actions.add(factory.actions().setField(factory.oxms().arpOp(ArpOpcode.REPLY)));
-        actions.add(factory.actions().setField(factory.oxms().arpSha(MacAddress.BROADCAST)));
-        actions.add(factory.actions().setField(factory.oxms().arpTha(MacAddress.BROADCAST)));
-        actions.add(factory.actions().setField(factory.oxms().arpSpa(IPv4Address.of("255.255.255.255"))));
-        actions.add(factory.actions().setField(factory.oxms().arpTpa(IPv4Address.of("255.255.255.255")))); 
-        fmb.setTableId(TableId.of(16)); */
-        
-        /* TP, IP OPT, VLAN TESTS   mb.setExact(MatchField.ETH_TYPE, EthType.IPv4);
-        mb.setExact(MatchField.VLAN_PCP, VlanPcp.of((byte) 1)); // might as well test these now too
-        //mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(512));
-        mb.setExact(MatchField.MPLS_LABEL, U32.of(32));
-        //mb.setExact(MatchField.MPLS_TC, U8.of((short)64));
-        mb.setExact(MatchField.IP_ECN, IpEcn.ECN_10); // and these
-        mb.setExact(MatchField.IP_DSCP, IpDscp.DSCP_16);
-        mb.setExact(MatchField.IP_PROTO, IpProtocol.SCTP); // with tcp, udp, sctp
-        mb.setExact(MatchField.SCTP_SRC, TransportPort.of(22));
-        mb.setExact(MatchField.SCTP_DST, TransportPort.of(80)); 
-        OFActionOutput.Builder actionBuilder = factory.actions().buildOutput();
-        actions.add(factory.actions().output(OFPort.of(1), Integer.MAX_VALUE));
-        actions.add(factory.actions().setField(factory.oxms().ethSrc(MacAddress.BROADCAST)));
-        actions.add(factory.actions().setField(factory.oxms().ethDst(MacAddress.BROADCAST)));
-        actions.add(factory.actions().setField(factory.oxms().ipv4Src(IPv4Address.of("127.0.1.2"))));
-        actions.add(factory.actions().setField(factory.oxms().ipv4Dst(IPv4Address.of("128.0.3.4")))); 
-        actions.add(factory.actions().setField(factory.oxms().sctpSrc(TransportPort.of(22))));
-        actions.add(factory.actions().setField(factory.oxms().sctpDst(TransportPort.of(80))));
-        actions.add(factory.actions().setField((factory.oxms().ipDscp(IpDscp.DSCP_11))));
-        actions.add(factory.actions().setField((factory.oxms().ipEcn(IpEcn.ECN_10))));
-
-        fmb.setTableId(TableId.of(7));
-        // these test non-set-field actions
-        //actions.add(factory.actions().copyTtlOut());
-        //actions.add(factory.actions().pushVlan(EthType.IPv4));
-        //actions.add(factory.actions().pushVlan(EthType.IPv4));
-        //actions.add(factory.actions().setField(factory.oxms().ipProto(IpProtocol.TCP))); // can't set protocol...makes sense */
-        
-        /* MPLS TESTS mb.setExact(MatchField.ETH_TYPE, EthType.MPLS_MULTICAST);
-        mb.setExact(MatchField.MPLS_LABEL, U32.of(18));
-        mb.setExact(MatchField.MPLS_TC, U8.of((short)4));
-        actions.add(factory.actions().output(OFPort.LOCAL, Integer.MAX_VALUE));
-        actions.add(factory.actions().setField(factory.oxms().mplsLabel(U32.ZERO)));
-        actions.add(factory.actions().setField(factory.oxms().mplsTc(U8.ZERO))); */
-        
-        /* METADATA TEST 
-        mb.setExact(MatchField.METADATA, OFMetadata.ofRaw(1)); 
-        //fmb.setActions(actions); // this will automatically create the apply actions instruction
-        applyActInstBldr.setActions(actions);
-        //mtrBldr.setMeterId(1);
-        instructions.add(applyActInstBldr.build());
-        //instructions.add(mtrBldr.build());
-        fmb.setInstructions(instructions);
-        fmb.setMatch(mb.build()); 
-		        
-		sfps.addFlow("test-flow", fmb.build(), switchId);
-		//sfps.deleteFlow("test-flow"); */
-		
-	}
-
-	@Override
-	public void switchRemoved(DatapathId switchId) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public void switchActivated(DatapathId switchId) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public void switchPortChanged(DatapathId switchId, OFPortDesc port,
-			PortChangeType type) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public void switchChanged(DatapathId switchId) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public void switchDeactivated(DatapathId switchId) {
-		// TODO Auto-generated method stub
-		
-	}
-
-}
diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
index dfbb7413271081918bae487a21b9cfd625dd4b2f..e5d09239933329a3775314d65d2a6fcded76bb6c 100644
--- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
+++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
@@ -26,11 +26,6 @@ import net.floodlightcontroller.core.types.NodePortTuple;
 import net.floodlightcontroller.core.util.SingletonTask;
 import net.floodlightcontroller.debugcounter.IDebugCounter;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventType;
-import net.floodlightcontroller.debugevent.IEventCategory;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService;
 import net.floodlightcontroller.packet.BSN;
@@ -64,1295 +59,1184 @@ import java.util.concurrent.TimeUnit;
  * through the topology.
  */
 public class TopologyManager implements IFloodlightModule, ITopologyService, IRoutingService, ILinkDiscoveryListener, IOFMessageListener {
+	protected static Logger log = LoggerFactory.getLogger(TopologyManager.class);
+	public static final String MODULE_NAME = "topology";
+
+	/**
+	 * Role of the controller.
+	 */
+	private HARole role;
+
+	/**
+	 * Set of ports for each switch
+	 */
+	protected Map<DatapathId, Set<OFPort>> switchPorts;
+
+	/**
+	 * Set of links organized by node port tuple
+	 */
+	protected Map<NodePortTuple, Set<Link>> switchPortLinks;
+
+	/**
+	 * Set of direct links
+	 */
+	protected Map<NodePortTuple, Set<Link>> directLinks;
+
+	/**
+	 * set of links that are broadcast domain links.
+	 */
+	protected Map<NodePortTuple, Set<Link>> portBroadcastDomainLinks;
+
+	/**
+	 * set of tunnel links
+	 */
+	protected Set<NodePortTuple> tunnelPorts;
+
+	protected ILinkDiscoveryService linkDiscoveryService;
+	protected IThreadPoolService threadPoolService;
+	protected IFloodlightProviderService floodlightProviderService;
+	protected IOFSwitchService switchService;
+	protected IRestApiService restApiService;
+	protected IDebugCounterService debugCounterService;
+
+	// Modules that listen to our updates
+	protected ArrayList<ITopologyListener> topologyAware;
+
+	protected BlockingQueue<LDUpdate> ldUpdates;
+
+	// These must be accessed using getCurrentInstance(), not directly
+	protected TopologyInstance currentInstance;
+	protected TopologyInstance currentInstanceWithoutTunnels;
+
+	protected SingletonTask newInstanceTask;
+	private Date lastUpdateTime;
+
+	/**
+	 * Flag that indicates if links (direct/tunnel/multihop links) were
+	 * updated as part of LDUpdate.
+	 */
+	protected boolean linksUpdated;
+	/**
+	 * Flag that indicates if direct or tunnel links were updated as
+	 * part of LDUpdate.
+	 */
+	protected boolean dtLinksUpdated;
+
+	/** Flag that indicates if tunnel ports were updated or not
+	 */
+	protected boolean tunnelPortsUpdated;
+
+	protected int TOPOLOGY_COMPUTE_INTERVAL_MS = 500;
+
+	private IHAListener haListener;
+
+	/**
+	 *  Debug Counters
+	 */
+	protected static final String PACKAGE = TopologyManager.class.getPackage().getName();
+	protected IDebugCounter ctrIncoming;
+
+	//  Getter/Setter methods
+	/**
+	 * Get the time interval for the period topology updates, if any.
+	 * The time returned is in milliseconds.
+	 * @return
+	 */
+	public int getTopologyComputeInterval() {
+		return TOPOLOGY_COMPUTE_INTERVAL_MS;
+	}
+
+	/**
+	 * Set the time interval for the period topology updates, if any.
+	 * The time is in milliseconds.
+	 * @return
+	 */
+	public void setTopologyComputeInterval(int time_ms) {
+		TOPOLOGY_COMPUTE_INTERVAL_MS = time_ms;
+	}
+
+	/**
+	 * Thread for recomputing topology.  The thread is always running,
+	 * however the function applyUpdates() has a blocking call.
+	 */
+	protected class UpdateTopologyWorker implements Runnable {
+		@Override
+		public void run() {
+			try {
+				if (ldUpdates.peek() != null) {
+					updateTopology();
+				}
+				handleMiscellaneousPeriodicEvents();
+			}
+			catch (Exception e) {
+				log.error("Error in topology instance task thread", e);
+			} finally {
+				if (floodlightProviderService.getRole() != HARole.STANDBY) {
+					newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS);
+				}
+			}
+		}
+	}
+
+	// To be used for adding any periodic events that's required by topology.
+	protected void handleMiscellaneousPeriodicEvents() {
+		return;
+	}
+
+	public boolean updateTopology() {
+		boolean newInstanceFlag;
+		linksUpdated = false;
+		dtLinksUpdated = false;
+		tunnelPortsUpdated = false;
+		List<LDUpdate> appliedUpdates = applyUpdates();
+		newInstanceFlag = createNewInstance("link-discovery-updates");
+		lastUpdateTime = new Date();
+		informListeners(appliedUpdates);
+		return newInstanceFlag;
+	}
+
+	// **********************
+	// ILinkDiscoveryListener
+	// **********************
+
+	@Override
+	public void linkDiscoveryUpdate(List<LDUpdate> updateList) {
+		if (log.isTraceEnabled()) {
+			log.trace("Queuing update: {}", updateList);
+		}
+		ldUpdates.addAll(updateList);
+	}
+
+	// ****************
+	// ITopologyService
+	// ****************
+
+	@Override
+	public Map<DatapathId, Set<Link>> getAllLinks(){
+
+		Map<DatapathId, Set<Link>> dpidLinks = new HashMap<DatapathId, Set<Link>>();
+		TopologyInstance ti = getCurrentInstance(true);
+		Set<DatapathId> switches = ti.getSwitches();
+
+		for(DatapathId s: switches) {
+			if (this.switchPorts.get(s) == null) continue;
+			for (OFPort p: switchPorts.get(s)) {
+				NodePortTuple np = new NodePortTuple(s, p);
+				if (this.switchPortLinks.get(np) == null) continue;
+				for(Link l: this.switchPortLinks.get(np)) {
+					if(dpidLinks.containsKey(s)) {
+						dpidLinks.get(s).add(l);
+					}
+					else {
+						dpidLinks.put(s,new HashSet<Link>(Arrays.asList(l)));
+					}
+
+				}
+			}
+		}
 
-    protected static Logger log = LoggerFactory.getLogger(TopologyManager.class);
-
-    public static final String MODULE_NAME = "topology";
-
-    public static final String CONTEXT_TUNNEL_ENABLED =
-            "com.bigswitch.floodlight.topologymanager.tunnelEnabled";
-
-    /**
-     * Role of the controller.
-     */
-    private HARole role;
-
-    /**
-     * Set of ports for each switch
-     */
-    protected Map<DatapathId, Set<OFPort>> switchPorts;
-
-    /**
-     * Set of links organized by node port tuple
-     */
-    protected Map<NodePortTuple, Set<Link>> switchPortLinks;
-
-    /**
-     * Set of direct links
-     */
-    protected Map<NodePortTuple, Set<Link>> directLinks;
-
-    /**
-     * set of links that are broadcast domain links.
-     */
-    protected Map<NodePortTuple, Set<Link>> portBroadcastDomainLinks;
-
-    /**
-     * set of tunnel links
-     */
-    protected Set<NodePortTuple> tunnelPorts;
-
-    protected ILinkDiscoveryService linkDiscoveryService;
-    protected IThreadPoolService threadPoolService;
-    protected IFloodlightProviderService floodlightProviderService;
-    protected IOFSwitchService switchService;
-    protected IRestApiService restApiService;
-    protected IDebugCounterService debugCounterService;
-
-    // Modules that listen to our updates
-    protected ArrayList<ITopologyListener> topologyAware;
-
-    protected BlockingQueue<LDUpdate> ldUpdates;
-
-    // These must be accessed using getCurrentInstance(), not directly
-    protected TopologyInstance currentInstance;
-    protected TopologyInstance currentInstanceWithoutTunnels;
-
-    protected SingletonTask newInstanceTask;
-    private Date lastUpdateTime;
-
-    /**
-     * Flag that indicates if links (direct/tunnel/multihop links) were
-     * updated as part of LDUpdate.
-     */
-    protected boolean linksUpdated;
-    /**
-     * Flag that indicates if direct or tunnel links were updated as
-     * part of LDUpdate.
-     */
-    protected boolean dtLinksUpdated;
-
-    /** Flag that indicates if tunnel ports were updated or not
-     */
-    protected boolean tunnelPortsUpdated;
-
-    protected int TOPOLOGY_COMPUTE_INTERVAL_MS = 500;
-
-    private IHAListener haListener;
-
-    /**
-     *  Debug Counters
-     */
-    protected static final String PACKAGE = TopologyManager.class.getPackage().getName();
-    protected IDebugCounter ctrIncoming;
-
-    /**
-     * Debug Events
-     */
-    protected IDebugEventService debugEventService;
-
-    /*
-     * Topology Event Updater
-     */
-    protected IEventCategory<TopologyEvent> eventCategory;
-
-    /**
-     * Topology Information exposed for a Topology related event - used inside
-     * the BigTopologyEvent class
-     */
-    protected class TopologyEventInfo {
-        private final int numOpenflowClustersWithTunnels;
-        private final int numOpenflowClustersWithoutTunnels;
-        private final Map<DatapathId, List<NodePortTuple>> externalPortsMap;
-        private final int numTunnelPorts;
-
-        public TopologyEventInfo(int numOpenflowClustersWithTunnels,
-                                 int numOpenflowClustersWithoutTunnels,
-                                 Map<DatapathId, List<NodePortTuple>> externalPortsMap,
-                                 int numTunnelPorts) {
-            super();
-            this.numOpenflowClustersWithTunnels = numOpenflowClustersWithTunnels;
-            this.numOpenflowClustersWithoutTunnels = numOpenflowClustersWithoutTunnels;
-            this.externalPortsMap = externalPortsMap;
-            this.numTunnelPorts = numTunnelPorts;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-            builder.append("# Openflow Clusters:");
-            builder.append(" { With Tunnels: ");
-            builder.append(numOpenflowClustersWithTunnels);
-            builder.append(" Without Tunnels: ");
-            builder.append(numOpenflowClustersWithoutTunnels);
-            builder.append(" }");
-            builder.append(", # External Clusters: ");
-            int numExternalClusters = externalPortsMap.size();
-            builder.append(numExternalClusters);
-            if (numExternalClusters > 0) {
-                builder.append(" { ");
-                int count = 0;
-                for (DatapathId extCluster : externalPortsMap.keySet()) {
-                    builder.append("#" + extCluster + ":Ext Ports: ");
-                    builder.append(externalPortsMap.get(extCluster).size());
-                    if (++count < numExternalClusters) {
-                        builder.append(", ");
-                    } else {
-                        builder.append(" ");
-                    }
-                }
-                builder.append("}");
-            }
-            builder.append(", # Tunnel Ports: ");
-            builder.append(numTunnelPorts);
-            return builder.toString();
-        }
-    }
-
-    /**
-     * Topology Event class to track topology related events
-     */
-    protected class TopologyEvent {
-        @EventColumn(name = "Reason", description = EventFieldType.STRING)
-        private final String reason;
-        @EventColumn(name = "Topology Summary")
-        private final TopologyEventInfo topologyInfo;
-
-        public TopologyEvent(String reason,
-                             TopologyEventInfo topologyInfo) {
-            super();
-            this.reason = reason;
-            this.topologyInfo = topologyInfo;
-        }
-    }
-
-    //  Getter/Setter methods
-
-    /**
-     * Get the time interval for the period topology updates, if any.
-     * The time returned is in milliseconds.
-     * @return
-     */
-    public int getTopologyComputeInterval() {
-        return TOPOLOGY_COMPUTE_INTERVAL_MS;
-    }
-
-    /**
-     * Set the time interval for the period topology updates, if any.
-     * The time is in milliseconds.
-     * @return
-     */
-    public void setTopologyComputeInterval(int time_ms) {
-        TOPOLOGY_COMPUTE_INTERVAL_MS = time_ms;
-    }
-
-    /**
-     * Thread for recomputing topology.  The thread is always running,
-     * however the function applyUpdates() has a blocking call.
-     */
-    protected class UpdateTopologyWorker implements Runnable {
-        @Override
-        public void run() {
-            try {
-                if (ldUpdates.peek() != null) {
-                    updateTopology();
-                }
-                handleMiscellaneousPeriodicEvents();
-            } catch (Exception e) {
-                log.error("Error in topology instance task thread", e);
-            } finally {
-                if (floodlightProviderService.getRole() != HARole.STANDBY) {
-                    newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS);
-                }
-            }
-        }
-    }
-
-    // To be used for adding any periodic events that's required by topology.
-    protected void handleMiscellaneousPeriodicEvents() {
-        return;
-    }
-
-    public boolean updateTopology() {
-        boolean newInstanceFlag;
-        linksUpdated = false;
-        dtLinksUpdated = false;
-        tunnelPortsUpdated = false;
-        List<LDUpdate> appliedUpdates = applyUpdates();
-        newInstanceFlag = createNewInstance("link-discovery-updates");
-        lastUpdateTime = new Date();
-        informListeners(appliedUpdates);
-        return newInstanceFlag;
-    }
-
-    // **********************
-    // ILinkDiscoveryListener
-    // **********************
-
-    @Override
-    public void linkDiscoveryUpdate(List<LDUpdate> updateList) {
-        if (log.isTraceEnabled()) {
-            log.trace("Queuing update: {}", updateList);
-        }
-        ldUpdates.addAll(updateList);
-    }
-
-    // ****************
-    // ITopologyService
-    // ****************
-
-    @Override
-    public Map<DatapathId, Set<Link>> getAllLinks() {
-
-        Map<DatapathId, Set<Link>> dpidLinks = new HashMap<DatapathId, Set<Link>>();
-        TopologyInstance ti = getCurrentInstance(true);
-        Set<DatapathId> switches = ti.getSwitches();
-
-        for (DatapathId s : switches) {
-            if (this.switchPorts.get(s) == null) continue;
-            for (OFPort p : switchPorts.get(s)) {
-                NodePortTuple np = new NodePortTuple(s, p);
-                if (this.switchPortLinks.get(np) == null) continue;
-                for (Link l : this.switchPortLinks.get(np)) {
-                    if (dpidLinks.containsKey(s)) {
-                        dpidLinks.get(s).add(l);
-                    } else {
-                        dpidLinks.put(s, new HashSet<Link>(Arrays.asList(l)));
-                    }
-
-                }
-            }
-        }
-
-        return dpidLinks;
-    }
-
-    @Override
-    public boolean isEdge(DatapathId sw, OFPort p) {
-        TopologyInstance ti = getCurrentInstance(true);
-        return ti.isEdge(sw, p);
-    }
-
-    @Override
-    public Set<OFPort> getSwitchBroadcastPorts(DatapathId sw) {
-        TopologyInstance ti = getCurrentInstance(true);
-        return ti.swBroadcastPorts(sw);
-    }
-
-    @Override
-    public Date getLastUpdateTime() {
-        return lastUpdateTime;
-    }
-
-    @Override
-    public void addListener(ITopologyListener listener) {
-        topologyAware.add(listener);
-    }
-
-    @Override
-    public boolean isAttachmentPointPort(DatapathId switchid, OFPort port) {
-        return isAttachmentPointPort(switchid, port, true);
-    }
-
-    @Override
-    public boolean isAttachmentPointPort(DatapathId switchid, OFPort port, boolean tunnelEnabled) {
-
-        // If the switch port is 'tun-bsn' port, it is not
-        // an attachment point port, irrespective of whether
-        // a link is found through it or not.
-        if (linkDiscoveryService.isTunnelPort(switchid, port))
-            return false;
-
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-
-        // if the port is not attachment point port according to
-        // topology instance, then return false
-        if (ti.isAttachmentPointPort(switchid, port) == false)
-            return false;
-
-        // Check whether the port is a physical port. We should not learn
-        // attachment points on "special" ports.
-        if ((port.getShortPortNumber() & 0xff00) == 0xff00 && port.getShortPortNumber() != (short) 0xfffe) return false;
-
-        // Make sure that the port is enabled.
-        IOFSwitch sw = switchService.getActiveSwitch(switchid);
-        if (sw == null) return false;
-        return (sw.portEnabled(port));
-    }
-
-    @Override
-    public DatapathId getOpenflowDomainId(DatapathId switchId) {
-        return getOpenflowDomainId(switchId, true);
-    }
-
-    @Override
-    public DatapathId getOpenflowDomainId(DatapathId switchId, boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.getOpenflowDomainId(switchId);
-    }
-
-    @Override
-    public boolean inSameOpenflowDomain(DatapathId switch1, DatapathId switch2) {
-        return inSameOpenflowDomain(switch1, switch2, true);
-    }
-
-    @Override
-    public boolean inSameOpenflowDomain(DatapathId switch1, DatapathId switch2,
-                                        boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.inSameOpenflowDomain(switch1, switch2);
-    }
-
-    @Override
-    public boolean isAllowed(DatapathId sw, OFPort portId) {
-        return isAllowed(sw, portId, true);
-    }
-
-    @Override
-    public boolean isAllowed(DatapathId sw, OFPort portId, boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.isAllowed(sw, portId);
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-    @Override
-    public boolean isIncomingBroadcastAllowed(DatapathId sw, OFPort portId) {
-        return isIncomingBroadcastAllowed(sw, portId, true);
-    }
-
-    @Override
-    public boolean isIncomingBroadcastAllowed(DatapathId sw, OFPort portId,
-                                              boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.isIncomingBroadcastAllowedOnSwitchPort(sw, portId);
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-
-    /** Get all the ports connected to the switch */
-    @Override
-    public Set<OFPort> getPortsWithLinks(DatapathId sw) {
-        return getPortsWithLinks(sw, true);
-    }
-
-    /** Get all the ports connected to the switch */
-    @Override
-    public Set<OFPort> getPortsWithLinks(DatapathId sw, boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.getPortsWithLinks(sw);
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-
-    /** Get all the ports on the target switch (targetSw) on which a
-     * broadcast packet must be sent from a host whose attachment point
-     * is on switch port (src, srcPort).
-     */
-    @Override
-    public Set<OFPort> getBroadcastPorts(DatapathId targetSw,
-                                         DatapathId src, OFPort srcPort) {
-        return getBroadcastPorts(targetSw, src, srcPort, true);
-    }
-
-    /** Get all the ports on the target switch (targetSw) on which a
-     * broadcast packet must be sent from a host whose attachment point
-     * is on switch port (src, srcPort).
-     */
-    @Override
-    public Set<OFPort> getBroadcastPorts(DatapathId targetSw,
-                                         DatapathId src, OFPort srcPort,
-                                         boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.getBroadcastPorts(targetSw, src, srcPort);
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-    @Override
-    public NodePortTuple getOutgoingSwitchPort(DatapathId src, OFPort srcPort,
-                                               DatapathId dst, OFPort dstPort) {
-        // Use this function to redirect traffic if needed.
-        return getOutgoingSwitchPort(src, srcPort, dst, dstPort, true);
-    }
-
-    @Override
-    public NodePortTuple getOutgoingSwitchPort(DatapathId src, OFPort srcPort,
-                                               DatapathId dst, OFPort dstPort,
-                                               boolean tunnelEnabled) {
-        // Use this function to redirect traffic if needed.
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.getOutgoingSwitchPort(src, srcPort,
-                dst, dstPort);
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-    @Override
-    public NodePortTuple getIncomingSwitchPort(DatapathId src, OFPort srcPort,
-                                               DatapathId dst, OFPort dstPort) {
-        return getIncomingSwitchPort(src, srcPort, dst, dstPort, true);
-    }
-
-    @Override
-    public NodePortTuple getIncomingSwitchPort(DatapathId src, OFPort srcPort,
-                                               DatapathId dst, OFPort dstPort,
-                                               boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.getIncomingSwitchPort(src, srcPort,
-                dst, dstPort);
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-
-    /**
-     * Checks if the two switchports belong to the same broadcast domain.
-     */
-    @Override
-    public boolean isInSameBroadcastDomain(DatapathId s1, OFPort p1, DatapathId s2,
-                                           OFPort p2) {
-        return isInSameBroadcastDomain(s1, p1, s2, p2, true);
-
-    }
-
-    @Override
-    public boolean isInSameBroadcastDomain(DatapathId s1, OFPort p1,
-                                           DatapathId s2, OFPort p2,
-                                           boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.inSameBroadcastDomain(s1, p1, s2, p2);
-
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-
-    /**
-     * Checks if the switchport is a broadcast domain port or not.
-     */
-    @Override
-    public boolean isBroadcastDomainPort(DatapathId sw, OFPort port) {
-        return isBroadcastDomainPort(sw, port, true);
-    }
-
-    @Override
-    public boolean isBroadcastDomainPort(DatapathId sw, OFPort port,
-                                         boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.isBroadcastDomainPort(new NodePortTuple(sw, port));
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-
-    /**
-     * Checks if the new attachment point port is consistent with the
-     * old attachment point port.
-     */
-    @Override
-    public boolean isConsistent(DatapathId oldSw, OFPort oldPort,
-                                DatapathId newSw, OFPort newPort) {
-        return isConsistent(oldSw, oldPort,
-                newSw, newPort, true);
-    }
-
-    @Override
-    public boolean isConsistent(DatapathId oldSw, OFPort oldPort,
-                                DatapathId newSw, OFPort newPort,
-                                boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.isConsistent(oldSw, oldPort, newSw, newPort);
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-    @Override
-    public NodePortTuple getAllowedOutgoingBroadcastPort(DatapathId src,
-                                                         OFPort srcPort,
-                                                         DatapathId dst,
-                                                         OFPort dstPort) {
-        return getAllowedOutgoingBroadcastPort(src, srcPort,
-                dst, dstPort, true);
-    }
-
-    @Override
-    public NodePortTuple getAllowedOutgoingBroadcastPort(DatapathId src,
-                                                         OFPort srcPort,
-                                                         DatapathId dst,
-                                                         OFPort dstPort,
-                                                         boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.getAllowedOutgoingBroadcastPort(src, srcPort,
-                dst, dstPort);
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-    @Override
-    public NodePortTuple
-    getAllowedIncomingBroadcastPort(DatapathId src, OFPort srcPort) {
-        return getAllowedIncomingBroadcastPort(src, srcPort, true);
-    }
-
-    @Override
-    public NodePortTuple
-    getAllowedIncomingBroadcastPort(DatapathId src, OFPort srcPort,
-                                    boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.getAllowedIncomingBroadcastPort(src, srcPort);
-    }
-
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-    @Override
-    public Set<DatapathId> getSwitchesInOpenflowDomain(DatapathId switchDPID) {
-        return getSwitchesInOpenflowDomain(switchDPID, true);
-    }
-
-    @Override
-    public Set<DatapathId> getSwitchesInOpenflowDomain(DatapathId switchDPID,
-                                                       boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.getSwitchesInOpenflowDomain(switchDPID);
-    }
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-
-    @Override
-    public Set<NodePortTuple> getBroadcastDomainPorts() {
-        return portBroadcastDomainLinks.keySet();
-    }
-
-    @Override
-    public Set<NodePortTuple> getTunnelPorts() {
-        return tunnelPorts;
-    }
-
-    @Override
-    public Set<NodePortTuple> getBlockedPorts() {
-        Set<NodePortTuple> bp;
-        Set<NodePortTuple> blockedPorts =
-                new HashSet<NodePortTuple>();
-
-        // As we might have two topologies, simply get the union of
-        // both of them and send it.
-        bp = getCurrentInstance(true).getBlockedPorts();
-        if (bp != null)
-            blockedPorts.addAll(bp);
-
-        bp = getCurrentInstance(false).getBlockedPorts();
-        if (bp != null)
-            blockedPorts.addAll(bp);
-
-        return blockedPorts;
-    }
-    ////////////////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////
-
-    // ***************
-    // IRoutingService
-    // ***************
-
-    @Override
-    public Route getRoute(DatapathId src, DatapathId dst, U64 cookie) {
-        return getRoute(src, dst, cookie, true);
-    }
-
-    @Override
-    public Route getRoute(DatapathId src, DatapathId dst, U64 cookie, boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.getRoute(src, dst, cookie);
-    }
-
-    @Override
-    public Route getRoute(DatapathId src, OFPort srcPort, DatapathId dst, OFPort dstPort, U64 cookie) {
-        return getRoute(src, srcPort, dst, dstPort, cookie, true);
-    }
-
-    @Override
-    public Route getRoute(DatapathId src, OFPort srcPort, DatapathId dst, OFPort dstPort, U64 cookie,
-                          boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.getRoute(src, srcPort, dst, dstPort, cookie);
-    }
-
-    @Override
-    public boolean routeExists(DatapathId src, DatapathId dst) {
-        return routeExists(src, dst, true);
-    }
-
-    @Override
-    public boolean routeExists(DatapathId src, DatapathId dst, boolean tunnelEnabled) {
-        TopologyInstance ti = getCurrentInstance(tunnelEnabled);
-        return ti.routeExists(src, dst);
-    }
-
-    @Override
-    public ArrayList<Route> getRoutes(DatapathId srcDpid, DatapathId dstDpid,
-                                      boolean tunnelEnabled) {
-        // Floodlight supports single path routing now
-
-        // return single path now
-        ArrayList<Route> result = new ArrayList<Route>();
-        result.add(getRoute(srcDpid, dstDpid, U64.of(0), tunnelEnabled));
-        return result;
-    }
-
-    // ******************
-    // IOFMessageListener
-    // ******************
-
-    @Override
-    public String getName() {
-        return MODULE_NAME;
-    }
-
-    @Override
-    public boolean isCallbackOrderingPrereq(OFType type, String name) {
-        return "linkdiscovery".equals(name);
-    }
-
-    @Override
-    public boolean isCallbackOrderingPostreq(OFType type, String name) {
-        return false;
-    }
-
-    @Override
-    public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
-        switch (msg.getType()) {
-            case PACKET_IN:
-                ctrIncoming.increment();
-                return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx);
-            default:
-                break;
-        }
-
-        return Command.CONTINUE;
-    }
-
-    // ***************
-    // IHAListener
-    // ***************
-
-    private class HAListenerDelegate implements IHAListener {
-        @Override
-        public void transitionToActive() {
-            role = HARole.ACTIVE;
-            log.debug("Re-computing topology due " +
-                    "to HA change from STANDBY->ACTIVE");
-            newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS,
-                    TimeUnit.MILLISECONDS);
-        }
-
-        @Override
-        public void controllerNodeIPsChanged(
-                Map<String, String> curControllerNodeIPs,
-                Map<String, String> addedControllerNodeIPs,
-                Map<String, String> removedControllerNodeIPs) {
-            // no-op
-        }
-
-        @Override
-        public String getName() {
-            return TopologyManager.this.getName();
-        }
-
-        @Override
-        public boolean isCallbackOrderingPrereq(HAListenerTypeMarker type,
-                                                String name) {
-            return "linkdiscovery".equals(name) ||
-                    "tunnelmanager".equals(name);
-        }
-
-        @Override
-        public boolean isCallbackOrderingPostreq(HAListenerTypeMarker type,
-                                                 String name) {
-            // TODO Auto-generated method stub
-            return false;
-        }
-
-        @Override
-        public void transitionToStandby() {
-            // TODO Auto-generated method stub
-
-        }
-    }
-
-    // *****************
-    // IFloodlightModule
-    // *****************
-
-    @Override
-    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
-        Collection<Class<? extends IFloodlightService>> l =
-                new ArrayList<Class<? extends IFloodlightService>>();
-        l.add(ITopologyService.class);
-        l.add(IRoutingService.class);
-        return l;
-    }
-
-    @Override
-    public Map<Class<? extends IFloodlightService>, IFloodlightService>
-    getServiceImpls() {
-        Map<Class<? extends IFloodlightService>,
-                IFloodlightService> m =
-                new HashMap<Class<? extends IFloodlightService>,
-                        IFloodlightService>();
-        // We are the class that implements the service
-        m.put(ITopologyService.class, this);
-        m.put(IRoutingService.class, this);
-        return m;
-    }
-
-    @Override
-    public Collection<Class<? extends IFloodlightService>>
-    getModuleDependencies() {
-        Collection<Class<? extends IFloodlightService>> l =
-                new ArrayList<Class<? extends IFloodlightService>>();
-        l.add(ILinkDiscoveryService.class);
-        l.add(IThreadPoolService.class);
-        l.add(IFloodlightProviderService.class);
-        l.add(IOFSwitchService.class);
-        l.add(IDebugCounterService.class);
-        l.add(IDebugEventService.class);
-        l.add(IRestApiService.class);
-        return l;
-    }
-
-    @Override
-    public void init(FloodlightModuleContext context)
-            throws FloodlightModuleException {
-        linkDiscoveryService = context.getServiceImpl(ILinkDiscoveryService.class);
-        threadPoolService = context.getServiceImpl(IThreadPoolService.class);
-        floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class);
-        switchService = context.getServiceImpl(IOFSwitchService.class);
-        restApiService = context.getServiceImpl(IRestApiService.class);
-        debugCounterService = context.getServiceImpl(IDebugCounterService.class);
-        debugEventService = context.getServiceImpl(IDebugEventService.class);
-
-        switchPorts = new HashMap<DatapathId, Set<OFPort>>();
-        switchPortLinks = new HashMap<NodePortTuple, Set<Link>>();
-        directLinks = new HashMap<NodePortTuple, Set<Link>>();
-        portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>();
-        tunnelPorts = new HashSet<NodePortTuple>();
-        topologyAware = new ArrayList<ITopologyListener>();
-        ldUpdates = new LinkedBlockingQueue<LDUpdate>();
-        haListener = new HAListenerDelegate();
-        registerTopologyDebugCounters();
-        registerTopologyDebugEvents();
-    }
-
-    protected void registerTopologyDebugEvents() throws FloodlightModuleException {
-        if (debugEventService == null) {
-            log.error("debugEventService should not be null. Has IDebugEventService been loaded previously?");
-        }
-        eventCategory = debugEventService.buildEvent(TopologyEvent.class)
-                .setModuleName(PACKAGE)
-                .setEventName("topologyevent")
-                .setEventDescription("Topology Computation")
-                .setEventType(EventType.ALWAYS_LOG)
-                .setBufferCapacity(100)
-                .register();
-    }
-
-    @Override
-    public void startUp(FloodlightModuleContext context) {
-        clearCurrentTopology();
-        // Initialize role to floodlight provider role.
-        this.role = floodlightProviderService.getRole();
-
-        ScheduledExecutorService ses = threadPoolService.getScheduledExecutor();
-        newInstanceTask = new SingletonTask(ses, new UpdateTopologyWorker());
+		return dpidLinks;
+	}
+
+	@Override
+	public boolean isEdge(DatapathId sw, OFPort p){
+		TopologyInstance ti = getCurrentInstance(true);
+		return ti.isEdge(sw, p);
+	}
+
+	@Override
+	public Set<OFPort> getSwitchBroadcastPorts(DatapathId sw){
+		TopologyInstance ti = getCurrentInstance(true);
+		return ti.swBroadcastPorts(sw);
+	}
+
+	@Override
+	public Date getLastUpdateTime() {
+		return lastUpdateTime;
+	}
+
+	@Override
+	public void addListener(ITopologyListener listener) {
+		topologyAware.add(listener);
+	}
+
+	@Override
+	public boolean isAttachmentPointPort(DatapathId switchid, OFPort port) {
+		return isAttachmentPointPort(switchid, port, true);
+	}
+
+	@Override
+	public boolean isAttachmentPointPort(DatapathId switchid, OFPort port, boolean tunnelEnabled) {
+
+		// If the switch port is 'tun-bsn' port, it is not
+		// an attachment point port, irrespective of whether
+		// a link is found through it or not.
+		if (linkDiscoveryService.isTunnelPort(switchid, port))
+			return false;
+
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+
+		// if the port is not attachment point port according to
+		// topology instance, then return false
+		if (ti.isAttachmentPointPort(switchid, port) == false)
+			return false;
+
+		// Check whether the port is a physical port. We should not learn
+		// attachment points on "special" ports.
+		if ((port.getShortPortNumber() & 0xff00) == 0xff00 && port.getShortPortNumber() != (short)0xfffe) return false;
+
+		// Make sure that the port is enabled.
+		IOFSwitch sw = switchService.getActiveSwitch(switchid);
+		if (sw == null) return false;
+		return (sw.portEnabled(port));
+	}
+
+	@Override
+	public DatapathId getOpenflowDomainId(DatapathId switchId) {
+		return getOpenflowDomainId(switchId, true);
+	}
+
+	@Override
+	public DatapathId getOpenflowDomainId(DatapathId switchId, boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.getOpenflowDomainId(switchId);
+	}
+
+	@Override
+	public boolean inSameOpenflowDomain(DatapathId switch1, DatapathId switch2) {
+		return inSameOpenflowDomain(switch1, switch2, true);
+	}
+
+	@Override
+	public boolean inSameOpenflowDomain(DatapathId switch1, DatapathId switch2,
+			boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.inSameOpenflowDomain(switch1, switch2);
+	}
+
+	@Override
+	public boolean isAllowed(DatapathId sw, OFPort portId) {
+		return isAllowed(sw, portId, true);
+	}
+
+	@Override
+	public boolean isAllowed(DatapathId sw, OFPort portId, boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.isAllowed(sw, portId);
+	}
+
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	@Override
+	public boolean isIncomingBroadcastAllowed(DatapathId sw, OFPort portId) {
+		return isIncomingBroadcastAllowed(sw, portId, true);
+	}
+
+	@Override
+	public boolean isIncomingBroadcastAllowed(DatapathId sw, OFPort portId,
+			boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.isIncomingBroadcastAllowedOnSwitchPort(sw, portId);
+	}
+
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	/** Get all the ports connected to the switch */
+	@Override
+	public Set<OFPort> getPortsWithLinks(DatapathId sw) {
+		return getPortsWithLinks(sw, true);
+	}
+
+	/** Get all the ports connected to the switch */
+	@Override
+	public Set<OFPort> getPortsWithLinks(DatapathId sw, boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.getPortsWithLinks(sw);
+	}
+
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	/** Get all the ports on the target switch (targetSw) on which a
+	 * broadcast packet must be sent from a host whose attachment point
+	 * is on switch port (src, srcPort).
+	 */
+	@Override
+	public Set<OFPort> getBroadcastPorts(DatapathId targetSw,
+			DatapathId src, OFPort srcPort) {
+		return getBroadcastPorts(targetSw, src, srcPort, true);
+	}
+
+	/** Get all the ports on the target switch (targetSw) on which a
+	 * broadcast packet must be sent from a host whose attachment point
+	 * is on switch port (src, srcPort).
+	 */
+	@Override
+	public Set<OFPort> getBroadcastPorts(DatapathId targetSw,
+			DatapathId src, OFPort srcPort,
+			boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.getBroadcastPorts(targetSw, src, srcPort);
+	}
+
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	@Override
+	public NodePortTuple getOutgoingSwitchPort(DatapathId src, OFPort srcPort,
+			DatapathId dst, OFPort dstPort) {
+		// Use this function to redirect traffic if needed.
+		return getOutgoingSwitchPort(src, srcPort, dst, dstPort, true);
+	}
+
+	@Override
+	public NodePortTuple getOutgoingSwitchPort(DatapathId src, OFPort srcPort,
+			DatapathId dst, OFPort dstPort,
+			boolean tunnelEnabled) {
+		// Use this function to redirect traffic if needed.
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.getOutgoingSwitchPort(src, srcPort,
+				dst, dstPort);
+	}
+
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	@Override
+	public NodePortTuple getIncomingSwitchPort(DatapathId src, OFPort srcPort,
+			DatapathId dst, OFPort dstPort) {
+		return getIncomingSwitchPort(src, srcPort, dst, dstPort, true);
+	}
+
+	@Override
+	public NodePortTuple getIncomingSwitchPort(DatapathId src, OFPort srcPort,
+			DatapathId dst, OFPort dstPort,
+			boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.getIncomingSwitchPort(src, srcPort,
+				dst, dstPort);
+	}
+
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	/**
+	 * Checks if the two switchports belong to the same broadcast domain.
+	 */
+	@Override
+	public boolean isInSameBroadcastDomain(DatapathId s1, OFPort p1, DatapathId s2,
+			OFPort p2) {
+		return isInSameBroadcastDomain(s1, p1, s2, p2, true);
+
+	}
+
+	@Override
+	public boolean isInSameBroadcastDomain(DatapathId s1, OFPort p1,
+			DatapathId s2, OFPort p2,
+			boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.inSameBroadcastDomain(s1, p1, s2, p2);
+
+	}
+
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	/**
+	 * Checks if the switchport is a broadcast domain port or not.
+	 */
+	@Override
+	public boolean isBroadcastDomainPort(DatapathId sw, OFPort port) {
+		return isBroadcastDomainPort(sw, port, true);
+	}
+
+	@Override
+	public boolean isBroadcastDomainPort(DatapathId sw, OFPort port,
+			boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.isBroadcastDomainPort(new NodePortTuple(sw, port));
+	}
+
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	/**
+	 * Checks if the new attachment point port is consistent with the
+	 * old attachment point port.
+	 */
+	@Override
+	public boolean isConsistent(DatapathId oldSw, OFPort oldPort,
+			DatapathId newSw, OFPort newPort) {
+		return isConsistent(oldSw, oldPort,
+				newSw, newPort, true);
+	}
+
+	@Override
+	public boolean isConsistent(DatapathId oldSw, OFPort oldPort,
+			DatapathId newSw, OFPort newPort,
+			boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.isConsistent(oldSw, oldPort, newSw, newPort);
+	}
+
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	@Override
+	public NodePortTuple getAllowedOutgoingBroadcastPort(DatapathId src,
+			OFPort srcPort,
+			DatapathId dst,
+			OFPort dstPort) {
+		return getAllowedOutgoingBroadcastPort(src, srcPort,
+				dst, dstPort, true);
+	}
+
+	@Override
+	public NodePortTuple getAllowedOutgoingBroadcastPort(DatapathId src,
+			OFPort srcPort,
+			DatapathId dst,
+			OFPort dstPort,
+			boolean tunnelEnabled){
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.getAllowedOutgoingBroadcastPort(src, srcPort,
+				dst, dstPort);
+	}
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	@Override
+	public NodePortTuple
+	getAllowedIncomingBroadcastPort(DatapathId src, OFPort srcPort) {
+		return getAllowedIncomingBroadcastPort(src,srcPort, true);
+	}
+
+	@Override
+	public NodePortTuple
+	getAllowedIncomingBroadcastPort(DatapathId src, OFPort srcPort,
+			boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.getAllowedIncomingBroadcastPort(src,srcPort);
+	}
+
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+	@Override
+	public Set<DatapathId> getSwitchesInOpenflowDomain(DatapathId switchDPID) {
+		return getSwitchesInOpenflowDomain(switchDPID, true);
+	}
+
+	@Override
+	public Set<DatapathId> getSwitchesInOpenflowDomain(DatapathId switchDPID,
+			boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.getSwitchesInOpenflowDomain(switchDPID);
+	}
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+
+	@Override
+	public Set<NodePortTuple> getBroadcastDomainPorts() {
+		return portBroadcastDomainLinks.keySet();
+	}
+
+	@Override
+	public Set<NodePortTuple> getTunnelPorts() {
+		return tunnelPorts;
+	}
+
+	@Override
+	public Set<NodePortTuple> getBlockedPorts() {
+		Set<NodePortTuple> bp;
+		Set<NodePortTuple> blockedPorts =
+				new HashSet<NodePortTuple>();
+
+		// As we might have two topologies, simply get the union of
+		// both of them and send it.
+		bp = getCurrentInstance(true).getBlockedPorts();
+		if (bp != null)
+			blockedPorts.addAll(bp);
+
+		bp = getCurrentInstance(false).getBlockedPorts();
+		if (bp != null)
+			blockedPorts.addAll(bp);
+
+		return blockedPorts;
+	}
+	////////////////////////////////////////////////////////////////////////
+	////////////////////////////////////////////////////////////////////////
+
+	// ***************
+	// IRoutingService
+	// ***************
+
+	@Override
+	public Route getRoute(DatapathId src, DatapathId dst, U64 cookie) {
+		return getRoute(src, dst, cookie, true);
+	}
+
+	@Override
+	public Route getRoute(DatapathId src, DatapathId dst, U64 cookie, boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.getRoute(src, dst, cookie);
+	}
+
+	@Override
+	public Route getRoute(DatapathId src, OFPort srcPort, DatapathId dst, OFPort dstPort, U64 cookie) {
+		return getRoute(src, srcPort, dst, dstPort, cookie, true);
+	}
+
+	@Override
+	public Route getRoute(DatapathId src, OFPort srcPort, DatapathId dst, OFPort dstPort, U64 cookie,
+			boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.getRoute(src, srcPort, dst, dstPort, cookie);
+	}
+
+	@Override
+	public boolean routeExists(DatapathId src, DatapathId dst) {
+		return routeExists(src, dst, true);
+	}
+
+	@Override
+	public boolean routeExists(DatapathId src, DatapathId dst, boolean tunnelEnabled) {
+		TopologyInstance ti = getCurrentInstance(tunnelEnabled);
+		return ti.routeExists(src, dst);
+	}
+
+	@Override
+	public ArrayList<Route> getRoutes(DatapathId srcDpid, DatapathId dstDpid,
+			boolean tunnelEnabled) {
+		// Floodlight supports single path routing now
+
+		// return single path now
+		ArrayList<Route> result=new ArrayList<Route>();
+		result.add(getRoute(srcDpid, dstDpid, U64.of(0), tunnelEnabled));
+		return result;
+	}
+
+	// ******************
+	// IOFMessageListener
+	// ******************
+
+	@Override
+	public String getName() {
+		return MODULE_NAME;
+	}
+
+	@Override
+	public boolean isCallbackOrderingPrereq(OFType type, String name) {
+		return "linkdiscovery".equals(name);
+	}
+
+	@Override
+	public boolean isCallbackOrderingPostreq(OFType type, String name) {
+		return false;
+	}
+
+	@Override
+	public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+		switch (msg.getType()) {
+		case PACKET_IN:
+			ctrIncoming.increment();
+			return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx);
+		default:
+			break;
+		}
 
-        if (role != HARole.STANDBY) {
-            newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS);
-        }
+		return Command.CONTINUE;
+	}
+
+	// ***************
+	// IHAListener
+	// ***************
+
+	private class HAListenerDelegate implements IHAListener {
+		@Override
+		public void transitionToActive() {
+			role = HARole.ACTIVE;
+			log.debug("Re-computing topology due " +
+					"to HA change from STANDBY->ACTIVE");
+			newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS,
+					TimeUnit.MILLISECONDS);
+		}
 
-        linkDiscoveryService.addListener(this);
-        floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this);
-        floodlightProviderService.addHAListener(this.haListener);
-        addRestletRoutable();
-    }
+		@Override
+		public void controllerNodeIPsChanged(
+				Map<String, String> curControllerNodeIPs,
+				Map<String, String> addedControllerNodeIPs,
+				Map<String, String> removedControllerNodeIPs) {
+			// no-op
+		}
 
-    private void registerTopologyDebugCounters() throws FloodlightModuleException {
-        if (debugCounterService == null) {
-            log.error("debugCounterService should not be null. Has IDebugEventService been loaded previously?");
-        }
-        debugCounterService.registerModule(PACKAGE);
-        ctrIncoming = debugCounterService.registerCounter(
-                PACKAGE, "incoming",
-                "All incoming packets seen by this module");
-    }
+		@Override
+		public String getName() {
+			return TopologyManager.this.getName();
+		}
 
-    protected void addRestletRoutable() {
-        restApiService.addRestletRoutable(new TopologyWebRoutable());
-    }
+		@Override
+		public boolean isCallbackOrderingPrereq(HAListenerTypeMarker type,
+				String name) {
+			return "linkdiscovery".equals(name) ||
+					"tunnelmanager".equals(name);
+		}
 
-    // ****************
-    // Internal methods
-    // ****************
+		@Override
+		public boolean isCallbackOrderingPostreq(HAListenerTypeMarker type,
+				String name) {
+			// TODO Auto-generated method stub
+			return false;
+		}
 
-    /**
-     * If the packet-in switch port is disabled for all data traffic, then
-     * the packet will be dropped.  Otherwise, the packet will follow the
-     * normal processing chain.
-     * @param sw
-     * @param pi
-     * @param cntx
-     * @return
-     */
-    protected Command dropFilter(DatapathId sw, OFPacketIn pi,
-                                 FloodlightContext cntx) {
-        Command result = Command.CONTINUE;
-        OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
-
-        // If the input port is not allowed for data traffic, drop everything.
-        // BDDP packets will not reach this stage.
-        if (isAllowed(sw, inPort) == false) {
-            if (log.isTraceEnabled()) {
-                log.trace("Ignoring packet because of topology " +
-                        "restriction on switch={}, port={}", sw.getLong(), inPort.getPortNumber());
-                result = Command.STOP;
-            }
-        }
-        return result;
-    }
+		@Override
+		public void transitionToStandby() {
+			// TODO Auto-generated method stub
 
-    /**
-     * TODO This method must be moved to a layer below forwarding
-     * so that anyone can use it.
-     * @param packetData
-     * @param sw
-     * @param ports
-     * @param cntx
-     */
-    public void doMultiActionPacketOut(byte[] packetData, IOFSwitch sw,
-                                       Set<OFPort> ports,
-                                       FloodlightContext cntx) {
-
-        if (ports == null) return;
-        if (packetData == null || packetData.length <= 0) return;
-
-        //OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
-        OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
-        List<OFAction> actions = new ArrayList<OFAction>();
-        for (OFPort p : ports) {
-            //actions.add(new OFActionOutput(p, (short) 0));
-            actions.add(sw.getOFFactory().actions().output(p, 0));
-        }
+		}
+	}
+
+	// *****************
+	// IFloodlightModule
+	// *****************
+
+	@Override
+	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+		Collection<Class<? extends IFloodlightService>> l =
+				new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(ITopologyService.class);
+		l.add(IRoutingService.class);
+		return l;
+	}
+
+	@Override
+	public Map<Class<? extends IFloodlightService>, IFloodlightService>
+	getServiceImpls() {
+		Map<Class<? extends IFloodlightService>,
+		IFloodlightService> m =
+		new HashMap<Class<? extends IFloodlightService>,
+		IFloodlightService>();
+		// We are the class that implements the service
+		m.put(ITopologyService.class, this);
+		m.put(IRoutingService.class, this);
+		return m;
+	}
+
+	@Override
+	public Collection<Class<? extends IFloodlightService>>
+	getModuleDependencies() {
+		Collection<Class<? extends IFloodlightService>> l =
+				new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(ILinkDiscoveryService.class);
+		l.add(IThreadPoolService.class);
+		l.add(IFloodlightProviderService.class);
+		l.add(IOFSwitchService.class);
+		l.add(IDebugCounterService.class);
+		l.add(IRestApiService.class);
+		return l;
+	}
+
+	@Override
+	public void init(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+		linkDiscoveryService = context.getServiceImpl(ILinkDiscoveryService.class);
+		threadPoolService = context.getServiceImpl(IThreadPoolService.class);
+		floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class);
+		switchService = context.getServiceImpl(IOFSwitchService.class);
+		restApiService = context.getServiceImpl(IRestApiService.class);
+		debugCounterService = context.getServiceImpl(IDebugCounterService.class);
+
+		switchPorts = new HashMap<DatapathId, Set<OFPort>>();
+		switchPortLinks = new HashMap<NodePortTuple, Set<Link>>();
+		directLinks = new HashMap<NodePortTuple, Set<Link>>();
+		portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>();
+		tunnelPorts = new HashSet<NodePortTuple>();
+		topologyAware = new ArrayList<ITopologyListener>();
+		ldUpdates = new LinkedBlockingQueue<LDUpdate>();
+		haListener = new HAListenerDelegate();
+		registerTopologyDebugCounters();
+	}
+
+	@Override
+	public void startUp(FloodlightModuleContext context) {
+		clearCurrentTopology();
+		// Initialize role to floodlight provider role.
+		this.role = floodlightProviderService.getRole();
+
+		ScheduledExecutorService ses = threadPoolService.getScheduledExecutor();
+		newInstanceTask = new SingletonTask(ses, new UpdateTopologyWorker());
+
+		if (role != HARole.STANDBY) {
+			newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS);
+		}
 
-        // set actions
-        pob.setActions(actions);
-        // set action length
-        //po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * ports.size()));
-        // set buffer-id to BUFFER_ID_NONE
-        pob.setBufferId(OFBufferId.NO_BUFFER);
-        // set in-port to OFPP_NONE
-        pob.setInPort(OFPort.ZERO);
+		linkDiscoveryService.addListener(this);
+		floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this);
+		floodlightProviderService.addHAListener(this.haListener);
+		addRestletRoutable();
+	}
 
-        // set packet data
-        pob.setData(packetData);
+	private void registerTopologyDebugCounters() throws FloodlightModuleException {
+		if (debugCounterService == null) {
+			log.error("debugCounterService should not be null. Has IDebugEventService been loaded previously?");
+		}
+		debugCounterService.registerModule(PACKAGE);
+		ctrIncoming = debugCounterService.registerCounter(
+				PACKAGE, "incoming",
+				"All incoming packets seen by this module");
+	}
+
+	protected void addRestletRoutable() {
+		restApiService.addRestletRoutable(new TopologyWebRoutable());
+	}
+
+	// ****************
+	// Internal methods
+	// ****************
+	/**
+	 * If the packet-in switch port is disabled for all data traffic, then
+	 * the packet will be dropped.  Otherwise, the packet will follow the
+	 * normal processing chain.
+	 * @param sw
+	 * @param pi
+	 * @param cntx
+	 * @return
+	 */
+	protected Command dropFilter(DatapathId sw, OFPacketIn pi,
+			FloodlightContext cntx) {
+		Command result = Command.CONTINUE;
+		OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
+
+		// If the input port is not allowed for data traffic, drop everything.
+		// BDDP packets will not reach this stage.
+		if (isAllowed(sw, inPort) == false) {
+			if (log.isTraceEnabled()) {
+				log.trace("Ignoring packet because of topology " +
+						"restriction on switch={}, port={}", sw.getLong(), inPort.getPortNumber());
+				result = Command.STOP;
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * TODO This method must be moved to a layer below forwarding
+	 * so that anyone can use it.
+	 * @param packetData
+	 * @param sw
+	 * @param ports
+	 * @param cntx
+	 */
+	public void doMultiActionPacketOut(byte[] packetData, IOFSwitch sw,
+			Set<OFPort> ports,
+			FloodlightContext cntx) {
+
+		if (ports == null) return;
+		if (packetData == null || packetData.length <= 0) return;
+
+		//OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
+		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
+		List<OFAction> actions = new ArrayList<OFAction>();
+		for(OFPort p: ports) {
+			//actions.add(new OFActionOutput(p, (short) 0));
+			actions.add(sw.getOFFactory().actions().output(p, 0));
+		}
 
-        // compute and set packet length.
-        //short poLength = (short)(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + packetData.length);
+		// set actions
+		pob.setActions(actions);
+		// set action length
+		//po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * ports.size()));
+		// set buffer-id to BUFFER_ID_NONE
+		pob.setBufferId(OFBufferId.NO_BUFFER);
+		// set in-port to OFPP_NONE
+		pob.setInPort(OFPort.ZERO);
 
-        //po.setLength(poLength);
+		// set packet data
+		pob.setData(packetData);
 
-        //ctrIncoming.updatePktOutFMCounterStore(sw, po);
-        if (log.isTraceEnabled()) {
-            log.trace("write broadcast packet on switch-id={} " +
-                            "interaces={} packet-data={} packet-out={}",
-                    new Object[]{sw.getId(), ports, packetData, pob.build()});
-        }
-        sw.write(pob.build(), LogicalOFMessageCategory.MAIN);
-    }
+		// compute and set packet length.
+		//short poLength = (short)(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + packetData.length);
 
-    /**
-     * Get the set of ports to eliminate for sending out BDDP.  The method
-     * returns all the ports that are suppressed for link discovery on the
-     * switch.
-     * packets.
-     * @param sid
-     * @return
-     */
-    protected Set<OFPort> getPortsToEliminateForBDDP(DatapathId sid) {
-        Set<NodePortTuple> suppressedNptList = linkDiscoveryService.getSuppressLLDPsInfo();
-        if (suppressedNptList == null) return null;
-
-        Set<OFPort> resultPorts = new HashSet<OFPort>();
-        for (NodePortTuple npt : suppressedNptList) {
-            if (npt.getNodeId() == sid) {
-                resultPorts.add(npt.getPortId());
-            }
-        }
+		//po.setLength(poLength);
 
-        return resultPorts;
-    }
+		//ctrIncoming.updatePktOutFMCounterStore(sw, po);
+		if (log.isTraceEnabled()) {
+			log.trace("write broadcast packet on switch-id={} " +
+					"interaces={} packet-data={} packet-out={}",
+					new Object[] {sw.getId(), ports, packetData, pob.build()});
+		}
+		sw.write(pob.build(), LogicalOFMessageCategory.MAIN);
+	}
+
+	/**
+	 * Get the set of ports to eliminate for sending out BDDP.  The method
+	 * returns all the ports that are suppressed for link discovery on the
+	 * switch.
+	 * packets.
+	 * @param sid
+	 * @return
+	 */
+	protected Set<OFPort> getPortsToEliminateForBDDP(DatapathId sid) {
+		Set<NodePortTuple> suppressedNptList = linkDiscoveryService.getSuppressLLDPsInfo();
+		if (suppressedNptList == null) return null;
+
+		Set<OFPort> resultPorts = new HashSet<OFPort>();
+		for(NodePortTuple npt: suppressedNptList) {
+			if (npt.getNodeId() == sid) {
+				resultPorts.add(npt.getPortId());
+			}
+		}
 
-    /**
-     * The BDDP packets are forwarded out of all the ports out of an
-     * openflowdomain.  Get all the switches in the same openflow
-     * domain as the sw (disabling tunnels).  Then get all the
-     * external switch ports and send these packets out.
-     * @param pinSwitch
-     * @param pi
-     * @param cntx
-     */
-    protected void doFloodBDDP(DatapathId pinSwitch, OFPacketIn pi,
-                               FloodlightContext cntx) {
+		return resultPorts;
+	}
+
+	/**
+	 * The BDDP packets are forwarded out of all the ports out of an
+	 * openflowdomain.  Get all the switches in the same openflow
+	 * domain as the sw (disabling tunnels).  Then get all the
+	 * external switch ports and send these packets out.
+	 * @param sw
+	 * @param pi
+	 * @param cntx
+	 */
+	protected void doFloodBDDP(DatapathId pinSwitch, OFPacketIn pi,
+			FloodlightContext cntx) {
+
+		TopologyInstance ti = getCurrentInstance(false);
+
+		Set<DatapathId> switches = ti.getSwitchesInOpenflowDomain(pinSwitch);
+
+		if (switches == null)
+		{
+			// indicates no links are connected to the switches
+			switches = new HashSet<DatapathId>();
+			switches.add(pinSwitch);
+		}
 
-        TopologyInstance ti = getCurrentInstance(false);
+		for (DatapathId sid : switches) {
+			IOFSwitch sw = switchService.getSwitch(sid);
+			if (sw == null) continue;
+			Collection<OFPort> enabledPorts = sw.getEnabledPortNumbers();
+			if (enabledPorts == null)
+				continue;
+			Set<OFPort> ports = new HashSet<OFPort>();
+			ports.addAll(enabledPorts);
+
+			// all the ports known to topology // without tunnels.
+			// out of these, we need to choose only those that are
+			// broadcast port, otherwise, we should eliminate.
+			Set<OFPort> portsKnownToTopo = ti.getPortsWithLinks(sid);
+
+			if (portsKnownToTopo != null) {
+				for (OFPort p : portsKnownToTopo) {
+					NodePortTuple npt =
+							new NodePortTuple(sid, p);
+					if (ti.isBroadcastDomainPort(npt) == false) {
+						ports.remove(p);
+					}
+				}
+			}
+
+			Set<OFPort> portsToEliminate = getPortsToEliminateForBDDP(sid);
+			if (portsToEliminate != null) {
+				ports.removeAll(portsToEliminate);
+			}
+
+			// remove the incoming switch port
+			if (pinSwitch == sid) {
+				ports.remove((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)));
+			}
+
+			// we have all the switch ports to which we need to broadcast.
+			doMultiActionPacketOut(pi.getData(), sw, ports, cntx);
+		}
 
-        Set<DatapathId> switches = ti.getSwitchesInOpenflowDomain(pinSwitch);
+	}
 
-        if (switches == null) {
-            // indicates no links are connected to the switches
-            switches = new HashSet<DatapathId>();
-            switches.add(pinSwitch);
-        }
+	protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
+		// get the packet-in switch.
+		Ethernet eth =
+				IFloodlightProviderService.bcStore.
+				get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
 
-        for (DatapathId sid : switches) {
-            IOFSwitch sw = switchService.getSwitch(sid);
-            if (sw == null) continue;
-            Collection<OFPort> enabledPorts = sw.getEnabledPortNumbers();
-            if (enabledPorts == null)
-                continue;
-            Set<OFPort> ports = new HashSet<OFPort>();
-            ports.addAll(enabledPorts);
-
-            // all the ports known to topology // without tunnels.
-            // out of these, we need to choose only those that are
-            // broadcast port, otherwise, we should eliminate.
-            Set<OFPort> portsKnownToTopo = ti.getPortsWithLinks(sid);
-
-            if (portsKnownToTopo != null) {
-                for (OFPort p : portsKnownToTopo) {
-                    NodePortTuple npt =
-                            new NodePortTuple(sid, p);
-                    if (ti.isBroadcastDomainPort(npt) == false) {
-                        ports.remove(p);
-                    }
-                }
-            }
-
-            Set<OFPort> portsToEliminate = getPortsToEliminateForBDDP(sid);
-            if (portsToEliminate != null) {
-                ports.removeAll(portsToEliminate);
-            }
-
-            // remove the incoming switch port
-            if (pinSwitch == sid) {
-                ports.remove((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)));
-            }
-
-            // we have all the switch ports to which we need to broadcast.
-            doMultiActionPacketOut(pi.getData(), sw, ports, cntx);
-        }
+		if (eth.getPayload() instanceof BSN) {
+			BSN bsn = (BSN) eth.getPayload();
+			if (bsn == null) return Command.STOP;
+			if (bsn.getPayload() == null) return Command.STOP;
 
-    }
+			// It could be a packet other than BSN LLDP, therefore
+			// continue with the regular processing.
+			if (bsn.getPayload() instanceof LLDP == false)
+				return Command.CONTINUE;
 
-    protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
-        // get the packet-in switch.
-        Ethernet eth =
-                IFloodlightProviderService.bcStore.
-                        get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
-
-        if (eth.getPayload() instanceof BSN) {
-            BSN bsn = (BSN) eth.getPayload();
-            if (bsn == null) return Command.STOP;
-            if (bsn.getPayload() == null) return Command.STOP;
-
-            // It could be a packet other than BSN LLDP, therefore
-            // continue with the regular processing.
-            if (bsn.getPayload() instanceof LLDP == false)
-                return Command.CONTINUE;
-
-            doFloodBDDP(sw.getId(), pi, cntx);
-            return Command.STOP;
-        } else {
-            return dropFilter(sw.getId(), pi, cntx);
-        }
-    }
+			doFloodBDDP(sw.getId(), pi, cntx);
+			return Command.STOP;
+		} else {
+			return dropFilter(sw.getId(), pi, cntx);
+		}
+	}
+
+	/**
+	 * Updates concerning switch disconnect and port down are not processed.
+	 * LinkDiscoveryManager is expected to process those messages and send
+	 * multiple link removed messages.  However, all the updates from
+	 * LinkDiscoveryManager would be propagated to the listeners of topology.
+	 */
+	public List<LDUpdate> applyUpdates() {
+		List<LDUpdate> appliedUpdates = new ArrayList<LDUpdate>();
+		LDUpdate update = null;
+		while (ldUpdates.peek() != null) {
+			try {
+				update = ldUpdates.take();
+			} catch (Exception e) {
+				log.error("Error reading link discovery update.", e);
+			}
+			if (log.isDebugEnabled()) {
+				log.debug("Applying update: {}", update);
+			}
+
+			switch (update.getOperation()) {
+			case LINK_UPDATED:
+				addOrUpdateLink(update.getSrc(), update.getSrcPort(),
+						update.getDst(), update.getDstPort(),
+						update.getLatency(), update.getType());
+				break;
+			case LINK_REMOVED:
+				removeLink(update.getSrc(), update.getSrcPort(),
+						update.getDst(), update.getDstPort());
+				break;
+			case SWITCH_UPDATED:
+				addOrUpdateSwitch(update.getSrc());
+				break;
+			case SWITCH_REMOVED:
+				removeSwitch(update.getSrc());
+				break;
+			case TUNNEL_PORT_ADDED:
+				addTunnelPort(update.getSrc(), update.getSrcPort());
+				break;
+			case TUNNEL_PORT_REMOVED:
+				removeTunnelPort(update.getSrc(), update.getSrcPort());
+				break;
+			case PORT_UP: case PORT_DOWN:
+				break;
+			}
+			// Add to the list of applied updates.
+			appliedUpdates.add(update);
+		}
+		return (Collections.unmodifiableList(appliedUpdates));
+	}
 
-    /**
-     * Updates concerning switch disconnect and port down are not processed.
-     * LinkDiscoveryManager is expected to process those messages and send
-     * multiple link removed messages.  However, all the updates from
-     * LinkDiscoveryManager would be propagated to the listeners of topology.
-     */
-    public List<LDUpdate> applyUpdates() {
-        List<LDUpdate> appliedUpdates = new ArrayList<LDUpdate>();
-        LDUpdate update = null;
-        while (ldUpdates.peek() != null) {
-            try {
-                update = ldUpdates.take();
-            } catch (Exception e) {
-                log.error("Error reading link discovery update.", e);
-            }
-            if (log.isDebugEnabled()) {
-                log.debug("Applying update: {}", update);
-            }
-
-            switch (update.getOperation()) {
-                case LINK_UPDATED:
-                    addOrUpdateLink(update.getSrc(), update.getSrcPort(),
-                            update.getDst(), update.getDstPort(),
-                            update.getLatency(), update.getType());
-                    break;
-                case LINK_REMOVED:
-                    removeLink(update.getSrc(), update.getSrcPort(),
-                            update.getDst(), update.getDstPort());
-                    break;
-                case SWITCH_UPDATED:
-                    addOrUpdateSwitch(update.getSrc());
-                    break;
-                case SWITCH_REMOVED:
-                    removeSwitch(update.getSrc());
-                    break;
-                case TUNNEL_PORT_ADDED:
-                    addTunnelPort(update.getSrc(), update.getSrcPort());
-                    break;
-                case TUNNEL_PORT_REMOVED:
-                    removeTunnelPort(update.getSrc(), update.getSrcPort());
-                    break;
-                case PORT_UP:
-                case PORT_DOWN:
-                    break;
-            }
-            // Add to the list of applied updates.
-            appliedUpdates.add(update);
-        }
-        return (Collections.unmodifiableList(appliedUpdates));
-    }
+	protected void addOrUpdateSwitch(DatapathId sw) {
+		/*TODO react appropriately
 
-    protected void addOrUpdateSwitch(DatapathId sw) {
-        /*TODO react appropriately
 		addSwitch(sw);
 		for (OFPortDesc p : switchService.getSwitch(sw).getPorts()) {
 			addPortToSwitch(sw, p.getPortNo());
 		}
 		*/
-        return;
-    }
-
-    public void addTunnelPort(DatapathId sw, OFPort port) {
-        NodePortTuple npt = new NodePortTuple(sw, port);
-        tunnelPorts.add(npt);
-        tunnelPortsUpdated = true;
-    }
-
-    public void removeTunnelPort(DatapathId sw, OFPort port) {
-        NodePortTuple npt = new NodePortTuple(sw, port);
-        tunnelPorts.remove(npt);
-        tunnelPortsUpdated = true;
-    }
-
-    public boolean createNewInstance() {
-        return createNewInstance("internal");
-    }
-
-    /**
-     * This function computes a new topology instance.
-     * It ignores links connected to all broadcast domain ports
-     * and tunnel ports. The method returns if a new instance of
-     * topology was created or not.
-     */
-    protected boolean createNewInstance(String reason) {
-        Set<NodePortTuple> blockedPorts = new HashSet<NodePortTuple>();
-
-        if (!linksUpdated) return false;
-
-        Map<NodePortTuple, Set<Link>> openflowLinks;
-        openflowLinks =
-                new HashMap<NodePortTuple, Set<Link>>();
-        Set<NodePortTuple> nptList = switchPortLinks.keySet();
-
-        if (nptList != null) {
-            for (NodePortTuple npt : nptList) {
-                Set<Link> linkSet = switchPortLinks.get(npt);
-                if (linkSet == null) continue;
-                openflowLinks.put(npt, new HashSet<Link>(linkSet));
-            }
-        }
-
-        // Identify all broadcast domain ports.
-        // Mark any port that has inconsistent set of links
-        // as broadcast domain ports as well.
-        Set<NodePortTuple> broadcastDomainPorts =
-                identifyBroadcastDomainPorts();
-
-        // Remove all links incident on broadcast domain ports.
-        for (NodePortTuple npt : broadcastDomainPorts) {
-            if (switchPortLinks.get(npt) == null) continue;
-            for (Link link : switchPortLinks.get(npt)) {
-                removeLinkFromStructure(openflowLinks, link);
-            }
-        }
-
-        // Remove all tunnel links.
-        for (NodePortTuple npt : tunnelPorts) {
-            if (switchPortLinks.get(npt) == null) continue;
-            for (Link link : switchPortLinks.get(npt)) {
-                removeLinkFromStructure(openflowLinks, link);
-            }
-        }
-        //switchPorts contains only ports that are part of links. Calculation of broadcast ports needs set of all ports.
-        Map<DatapathId, Set<OFPort>> allPorts = new HashMap<DatapathId, Set<OFPort>>();
-        ;
-        for (DatapathId sw : switchPorts.keySet()) {
-            allPorts.put(sw, this.getPorts(sw));
-        }
-
-        TopologyInstance nt = new TopologyInstance(switchPorts,
-                blockedPorts,
-                openflowLinks,
-                broadcastDomainPorts,
-                tunnelPorts,
-                switchPortLinks,
-                allPorts,
-                portBroadcastDomainLinks);
-
-        nt.compute();
-
-        // We set the instances with and without tunnels to be identical.
-        // If needed, we may compute them differently.
-        currentInstance = nt;
-        currentInstanceWithoutTunnels = nt;
-
-        TopologyEventInfo topologyInfo =
-                new TopologyEventInfo(0, nt.getClusters().size(),
-                        new HashMap<DatapathId, List<NodePortTuple>>(),
-                        0);
-        eventCategory.newEventWithFlush(new TopologyEvent(reason, topologyInfo));
-
-        return true;
-    }
-
-    /**
-     *  We expect every switch port to have at most two links.  Both these
-     *  links must be unidirectional links connecting to the same switch port.
-     *  If not, we will mark this as a broadcast domain port.
-     */
-    protected Set<NodePortTuple> identifyBroadcastDomainPorts() {
-
-        Set<NodePortTuple> broadcastDomainPorts =
-                new HashSet<NodePortTuple>();
-        broadcastDomainPorts.addAll(this.portBroadcastDomainLinks.keySet());
-
-        Set<NodePortTuple> additionalNpt =
-                new HashSet<NodePortTuple>();
-
-        // Copy switchPortLinks
-        Map<NodePortTuple, Set<Link>> spLinks =
-                new HashMap<NodePortTuple, Set<Link>>();
-        for (NodePortTuple npt : switchPortLinks.keySet()) {
-            spLinks.put(npt, new HashSet<Link>(switchPortLinks.get(npt)));
-        }
+		return;
+	}
+
+	public void addTunnelPort(DatapathId sw, OFPort port) {
+		NodePortTuple npt = new NodePortTuple(sw, port);
+		tunnelPorts.add(npt);
+		tunnelPortsUpdated = true;
+	}
+
+	public void removeTunnelPort(DatapathId sw, OFPort port) {
+		NodePortTuple npt = new NodePortTuple(sw, port);
+		tunnelPorts.remove(npt);
+		tunnelPortsUpdated = true;
+	}
+
+	public boolean createNewInstance() {
+		return createNewInstance("internal");
+	}
+
+	/**
+	 * This function computes a new topology instance.
+	 * It ignores links connected to all broadcast domain ports
+	 * and tunnel ports. The method returns if a new instance of
+	 * topology was created or not.
+	 */
+	protected boolean createNewInstance(String reason) {
+		Set<NodePortTuple> blockedPorts = new HashSet<NodePortTuple>();
+
+		if (!linksUpdated) return false;
+
+		Map<NodePortTuple, Set<Link>> openflowLinks;
+		openflowLinks =
+				new HashMap<NodePortTuple, Set<Link>>();
+		Set<NodePortTuple> nptList = switchPortLinks.keySet();
+
+		if (nptList != null) {
+			for(NodePortTuple npt: nptList) {
+				Set<Link> linkSet = switchPortLinks.get(npt);
+				if (linkSet == null) continue;
+				openflowLinks.put(npt, new HashSet<Link>(linkSet));
+			}
+		}
 
-        for (NodePortTuple npt : spLinks.keySet()) {
-            Set<Link> links = spLinks.get(npt);
-            boolean bdPort = false;
-            ArrayList<Link> linkArray = new ArrayList<Link>();
-            if (links.size() > 2) {
-                bdPort = true;
-            } else if (links.size() == 2) {
-                for (Link l : links) {
-                    linkArray.add(l);
-                }
-                // now, there should be two links in [0] and [1].
-                Link l1 = linkArray.get(0);
-                Link l2 = linkArray.get(1);
-
-                // check if these two are symmetric.
-                if (!l1.getSrc().equals(l2.getDst()) ||
-                        !l1.getSrcPort().equals(l2.getDstPort()) ||
-                        !l1.getDst().equals(l2.getSrc()) ||
-                        !l1.getDstPort().equals(l2.getSrcPort())) {
-                    bdPort = true;
-                }
-            }
-
-            if (bdPort && (broadcastDomainPorts.contains(npt) == false)) {
-                additionalNpt.add(npt);
-            }
-        }
+		// Identify all broadcast domain ports.
+		// Mark any port that has inconsistent set of links
+		// as broadcast domain ports as well.
+		Set<NodePortTuple> broadcastDomainPorts =
+				identifyBroadcastDomainPorts();
+
+		// Remove all links incident on broadcast domain ports.
+		for (NodePortTuple npt : broadcastDomainPorts) {
+			if (switchPortLinks.get(npt) == null) continue;
+			for (Link link : switchPortLinks.get(npt)) {
+				removeLinkFromStructure(openflowLinks, link);
+			}
+		}
 
-        if (additionalNpt.size() > 0) {
-            log.warn("The following switch ports have multiple " +
-                    "links incident on them, so these ports will be treated " +
-                    " as braodcast domain ports. {}", additionalNpt);
+		// Remove all tunnel links.
+		for (NodePortTuple npt: tunnelPorts) {
+			if (switchPortLinks.get(npt) == null) continue;
+			for (Link link : switchPortLinks.get(npt)) {
+				removeLinkFromStructure(openflowLinks, link);
+			}
+		}
+		//switchPorts contains only ports that are part of links. Calculation of broadcast ports needs set of all ports. 
+		Map<DatapathId, Set<OFPort>> allPorts = new HashMap<DatapathId, Set<OFPort>>();;
+		for (DatapathId sw : switchPorts.keySet()){
+			allPorts.put(sw, this.getPorts(sw));
+		}
 
-            broadcastDomainPorts.addAll(additionalNpt);
-        }
-        return broadcastDomainPorts;
-    }
+		TopologyInstance nt = new TopologyInstance(switchPorts,
+				blockedPorts,
+				openflowLinks,
+				broadcastDomainPorts,
+				tunnelPorts,
+				switchPortLinks,
+				allPorts,
+				portBroadcastDomainLinks);
+
+		nt.compute();
+
+		// We set the instances with and without tunnels to be identical.
+		// If needed, we may compute them differently.
+		currentInstance = nt;
+		currentInstanceWithoutTunnels = nt;
+
+		return true;
+	}
+
+	/**
+	 *  We expect every switch port to have at most two links.  Both these
+	 *  links must be unidirectional links connecting to the same switch port.
+	 *  If not, we will mark this as a broadcast domain port.
+	 */
+	protected Set<NodePortTuple> identifyBroadcastDomainPorts() {
+
+		Set<NodePortTuple> broadcastDomainPorts =
+				new HashSet<NodePortTuple>();
+		broadcastDomainPorts.addAll(this.portBroadcastDomainLinks.keySet());
+
+		Set<NodePortTuple> additionalNpt =
+				new HashSet<NodePortTuple>();
+
+		// Copy switchPortLinks
+		Map<NodePortTuple, Set<Link>> spLinks =
+				new HashMap<NodePortTuple, Set<Link>>();
+		for (NodePortTuple npt : switchPortLinks.keySet()) {
+			spLinks.put(npt, new HashSet<Link>(switchPortLinks.get(npt)));
+		}
 
+		for (NodePortTuple npt : spLinks.keySet()) {
+			Set<Link> links = spLinks.get(npt);
+			boolean bdPort = false;
+			ArrayList<Link> linkArray = new ArrayList<Link>();
+			if (links.size() > 2) {
+				bdPort = true;
+			} else if (links.size() == 2) {
+				for (Link l : links) {
+					linkArray.add(l);
+				}
+				// now, there should be two links in [0] and [1].
+				Link l1 = linkArray.get(0);
+				Link l2 = linkArray.get(1);
+
+				// check if these two are symmetric.
+				if (!l1.getSrc().equals(l2.getDst()) ||
+						!l1.getSrcPort().equals(l2.getDstPort()) ||
+						!l1.getDst().equals(l2.getSrc()) ||
+						!l1.getDstPort().equals(l2.getSrcPort())) {
+					bdPort = true;
+				}
+			}
+
+			if (bdPort && (broadcastDomainPorts.contains(npt) == false)) {
+				additionalNpt.add(npt);
+			}
+		}
 
-    public void informListeners(List<LDUpdate> linkUpdates) {
+		if (additionalNpt.size() > 0) {
+			log.warn("The following switch ports have multiple " +
+					"links incident on them, so these ports will be treated " +
+					" as braodcast domain ports. {}", additionalNpt);
 
-        if (role != null && role != HARole.ACTIVE)
-            return;
+			broadcastDomainPorts.addAll(additionalNpt);
+		}
+		return broadcastDomainPorts;
+	}
 
-        for (int i = 0; i < topologyAware.size(); ++i) {
-            ITopologyListener listener = topologyAware.get(i);
-            listener.topologyChanged(linkUpdates);
-        }
-    }
 
-    public void addSwitch(DatapathId sid) {
-        if (switchPorts.containsKey(sid) == false) {
-            switchPorts.put(sid, new HashSet<OFPort>());
-        }
-    }
 
-    private void addPortToSwitch(DatapathId s, OFPort p) {
-        addSwitch(s);
-        switchPorts.get(s).add(p);
-    }
+	public void informListeners(List<LDUpdate> linkUpdates) {
 
-    public void removeSwitch(DatapathId sid) {
-        // Delete all the links in the switch, switch and all
-        // associated data should be deleted.
-        if (switchPorts.containsKey(sid) == false) return;
+		if (role != null && role != HARole.ACTIVE)
+			return;
 
-        // Check if any tunnel ports need to be removed.
-        for (NodePortTuple npt : tunnelPorts) {
-            if (npt.getNodeId() == sid) {
-                removeTunnelPort(npt.getNodeId(), npt.getPortId());
-            }
-        }
+		for(int i=0; i < topologyAware.size(); ++i) {
+			ITopologyListener listener = topologyAware.get(i);
+			listener.topologyChanged(linkUpdates);
+		}
+	}
 
-        Set<Link> linksToRemove = new HashSet<Link>();
-        for (OFPort p : switchPorts.get(sid)) {
-            NodePortTuple n1 = new NodePortTuple(sid, p);
-            linksToRemove.addAll(switchPortLinks.get(n1));
-        }
+	public void addSwitch(DatapathId sid) {
+		if (switchPorts.containsKey(sid) == false) {
+			switchPorts.put(sid, new HashSet<OFPort>());
+		}
+	}
+
+	private void addPortToSwitch(DatapathId s, OFPort p) {
+		addSwitch(s);
+		switchPorts.get(s).add(p);
+	}
+
+	public void removeSwitch(DatapathId sid) {
+		// Delete all the links in the switch, switch and all
+		// associated data should be deleted.
+		if (switchPorts.containsKey(sid) == false) return;
+
+		// Check if any tunnel ports need to be removed.
+		for(NodePortTuple npt: tunnelPorts) {
+			if (npt.getNodeId() == sid) {
+				removeTunnelPort(npt.getNodeId(), npt.getPortId());
+			}
+		}
 
-        if (linksToRemove.isEmpty()) return;
+		Set<Link> linksToRemove = new HashSet<Link>();
+		for(OFPort p: switchPorts.get(sid)) {
+			NodePortTuple n1 = new NodePortTuple(sid, p);
+			linksToRemove.addAll(switchPortLinks.get(n1));
+		}
 
-        for (Link link : linksToRemove) {
-            removeLink(link);
-        }
-    }
+		if (linksToRemove.isEmpty()) return;
 
-    /**
-     * Add the given link to the data structure.
-     * @param s
-     * @param l
-     */
-    private void addLinkToStructure(Map<NodePortTuple, Set<Link>> s, Link l) {
-        NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
-        NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort());
+		for(Link link: linksToRemove) {
+			removeLink(link);
+		}
+	}
+
+	/**
+	 * Add the given link to the data structure.
+	 * @param s
+	 * @param l
+	 */
+	private void addLinkToStructure(Map<NodePortTuple, Set<Link>> s, Link l) {
+		NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
+		NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort());
+
+		if (s.get(n1) == null) {
+			s.put(n1, new HashSet<Link>());
+		} 
+		if (s.get(n2) == null) {
+			s.put(n2, new HashSet<Link>());
+		}
 
-        if (s.get(n1) == null) {
-            s.put(n1, new HashSet<Link>());
-        }
-        if (s.get(n2) == null) {
-            s.put(n2, new HashSet<Link>());
-        }
 		/* 
 		 * Since we don't include latency in .equals(), we need
 		 * to explicitly remove the existing link (if present).
diff --git a/src/main/java/net/floodlightcontroller/util/ActionUtils.java b/src/main/java/net/floodlightcontroller/util/ActionUtils.java
index a787028df855dc41c0658a302597866f9f6b8bc4..68832bed235f1efb2d0e4a8a2186e245407f7734 100644
--- a/src/main/java/net/floodlightcontroller/util/ActionUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/ActionUtils.java
@@ -1,18 +1,26 @@
 package net.floodlightcontroller.util;
 
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
 import java.util.ArrayDeque;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.projectfloodlight.openflow.protocol.OFActionCopyField;
 import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFOxmList;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.action.OFActionEnqueue;
 import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
 import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
+import org.projectfloodlight.openflow.protocol.action.OFActionMeter;
 import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
 import org.projectfloodlight.openflow.protocol.action.OFActionPopMpls;
 import org.projectfloodlight.openflow.protocol.action.OFActionPushMpls;
@@ -34,6 +42,8 @@ import org.projectfloodlight.openflow.protocol.action.OFActionSetTpDst;
 import org.projectfloodlight.openflow.protocol.action.OFActionSetTpSrc;
 import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp;
 import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmActsetOutput;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpOp;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpSha;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmArpSpa;
@@ -52,17 +62,21 @@ import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpProto;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Dst;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Src;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Dst;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Exthdr;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Flabel;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdSll;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdTarget;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdTll;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Src;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmMetadata;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsBos;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsLabel;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsTc;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmPacketType;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmSctpDst;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmSctpSrc;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmTcpDst;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmTcpFlags;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmTcpSrc;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmUdpDst;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmUdpSrc;
@@ -80,17 +94,24 @@ import org.projectfloodlight.openflow.types.IpEcn;
 import org.projectfloodlight.openflow.types.IpProtocol;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBooleanValue;
-import org.projectfloodlight.openflow.types.OFGroup;
 import org.projectfloodlight.openflow.types.OFMetadata;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
 import org.projectfloodlight.openflow.types.TransportPort;
+import org.projectfloodlight.openflow.types.U16;
 import org.projectfloodlight.openflow.types.U32;
 import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.U8;
 import org.projectfloodlight.openflow.types.VlanPcp;
 import org.projectfloodlight.openflow.types.VlanVid;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.JsonGenerator.Feature;
+import com.fasterxml.jackson.core.JsonParser;
 
 /**
  * OFAction helper functions. Use with any OpenFlowJ-Loxi Action.
@@ -103,1229 +124,1248 @@ import org.slf4j.Logger;
  * @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu>
  */
 public class ActionUtils {
-	/* OF1.3 ACTIONS (includes OF1.0) */
-	public static final String STR_OUTPUT = "output";
-	public static final String STR_ENQUEUE = "enqueue";
-	public static final String STR_VLAN_STRIP = "strip_vlan";
-	public static final String STR_VLAN_POP = "pop_vlan";
-	public static final String STR_VLAN_PUSH = "push_vlan";
-	public static final String STR_VLAN_SET_PCP = "set_vlan_pcp";
-	public static final String STR_VLAN_SET_VID = "set_vlan_vid";
-	public static final String STR_QUEUE_SET = "set_queue";
-	public static final String STR_DL_SRC_SET = "set_eth_src";
-	public static final String STR_DL_DST_SET = "set_eth_dst";
-	public static final String STR_NW_SRC_SET = "set_ipv4_src";
-	public static final String STR_NW_DST_SET = "set_ipv4_dst";
-	public static final String STR_NW_ECN_SET = "set_ip_ecn";
-	public static final String STR_NW_TOS_SET = "set_ip_tos";
-	public static final String STR_NW_TTL_SET = "set_ip_ttl";
-	public static final String STR_NW_TTL_DEC = "dec_ip_ttl";
-	public static final String STR_TTL_IN_COPY = "copy_ip_ttl_in";
-	public static final String STR_TTL_OUT_COPY = "copy_ip_ttl_out";
-	public static final String STR_MPLS_LABEL_SET = "set_mpls_label";
-	public static final String STR_MPLS_TC_SET = "set_mpls_tc";
-	public static final String STR_MPLS_TTL_SET = "set_mpls_ttl";
-	public static final String STR_MPLS_TTL_DEC = "dec_mpls_ttl";
-	public static final String STR_MPLS_PUSH = "push_mpls";
-	public static final String STR_MPLS_POP = "pop_mpls";
-	public static final String STR_TP_SRC_SET = "set_tp_src";
-	public static final String STR_TP_DST_SET = "set_tp_dst";
-	public static final String STR_PBB_PUSH = "push_pbb";
-	public static final String STR_PBB_POP = "pop_pbb";
-	public static final String STR_GROUP = "group";
-	public static final String STR_FIELD_SET = "set_field";
-	public static final String STR_EXPERIMENTER = "experimenter";
-	public static final String STR_NOT_APPLICABLE = "n/a";
+    private static final Logger log = LoggerFactory.getLogger(ActionUtils.class);
+
+    /* OF1.3 ACTIONS (includes OF1.0) */
+    public static final String STR_OUTPUT = "output";
+    public static final String STR_ENQUEUE = "enqueue";
+    public static final String STR_VLAN_STRIP = "strip_vlan";
+    public static final String STR_VLAN_POP = "pop_vlan";
+    public static final String STR_VLAN_PUSH = "push_vlan";
+    public static final String STR_VLAN_SET_PCP = "set_vlan_pcp";
+    public static final String STR_VLAN_SET_VID = "set_vlan_vid";
+    public static final String STR_QUEUE_SET = "set_queue";
+    public static final String STR_DL_SRC_SET = "set_eth_src";
+    public static final String STR_DL_DST_SET = "set_eth_dst";
+    public static final String STR_NW_SRC_SET = "set_ipv4_src";
+    public static final String STR_NW_DST_SET = "set_ipv4_dst";
+    public static final String STR_NW_ECN_SET = "set_ip_ecn";
+    public static final String STR_NW_TOS_SET = "set_ip_tos";
+    public static final String STR_NW_TTL_SET = "set_ip_ttl";
+    public static final String STR_NW_TTL_DEC = "dec_ip_ttl";
+    public static final String STR_TTL_IN_COPY = "copy_ip_ttl_in";
+    public static final String STR_TTL_OUT_COPY = "copy_ip_ttl_out";
+    public static final String STR_MPLS_LABEL_SET = "set_mpls_label";
+    public static final String STR_MPLS_TC_SET = "set_mpls_tc";
+    public static final String STR_MPLS_TTL_SET = "set_mpls_ttl";
+    public static final String STR_MPLS_TTL_DEC = "dec_mpls_ttl";
+    public static final String STR_MPLS_PUSH = "push_mpls";
+    public static final String STR_MPLS_POP = "pop_mpls";
+    public static final String STR_TP_SRC_SET = "set_tp_src";
+    public static final String STR_TP_DST_SET = "set_tp_dst";
+    public static final String STR_PBB_PUSH = "push_pbb";
+    public static final String STR_PBB_POP = "pop_pbb";
+    public static final String STR_GROUP = "group";
+    public static final String STR_FIELD_SET = "set_field";
+    public static final String STR_FIELD_COPY = "copy_field";
+    public static final String STR_METER = "meter";
+    public static final String STR_EXPERIMENTER = "experimenter";
+    public static final String STR_NOT_APPLICABLE = "n/a";
+
+    /* OF1.3 set-field operations are defined as any OF1.3 match.
+     * We will borrow MatchUtils's String definitions of all OF1.3
+     * set-field operations to be consistent.
+     */
+
+    private static final JsonFactory jsonFactory = new JsonFactory();
+    private static final String JSON_EMPTY_OBJECT = "{}";
+
+
+    /**
+     * Returns a String representation of all the OpenFlow actions.
+     * @param actions; A list of OFActions to encode into one string
+     * @return A dpctl-style string of the actions
+     */
+    public static String actionsToString(List<OFAction> actions) {
+        StringBuilder sb = new StringBuilder();
+        for (OFAction a : actions) {
+            if (sb.length() > 0) {
+                sb.append(',');
+            }
+            switch(a.getType()) {
+            case OUTPUT:
+                sb.append(STR_OUTPUT).append("=").append(Integer.toString(((OFActionOutput)a).getPort().getPortNumber()));
+                break;
+            case ENQUEUE:
+                long queue = ((OFActionEnqueue)a).getQueueId();
+                sb.append(STR_ENQUEUE).append("=").append(portToString(((OFActionEnqueue)a).getPort())).append(":0x").append(String.format("%02x", queue));
+                break;
+            case STRIP_VLAN:
+                sb.append(STR_VLAN_STRIP);
+                break;
+            case POP_VLAN:
+                sb.append(STR_VLAN_POP);
+                break;
+            case PUSH_VLAN:
+                sb.append(STR_VLAN_PUSH).append("=").append(Integer.toString(((OFActionPushVlan)a).getEthertype().getValue()));
+                break;
+            case SET_VLAN_VID:
+                sb.append(STR_VLAN_SET_VID).append("=").append(Short.toString(((OFActionSetVlanVid)a).getVlanVid().getVlan()));
+                break;
+            case SET_VLAN_PCP:
+                sb.append(STR_VLAN_SET_PCP).append("=").append(Byte.toString(((OFActionSetVlanPcp)a).getVlanPcp().getValue()));
+                break;
+            case SET_QUEUE:
+                sb.append(STR_QUEUE_SET).append("=").append(Long.toString(((OFActionSetQueue)a).getQueueId()));
+            case SET_DL_SRC:
+                sb.append(STR_DL_SRC_SET).append("=").append( ((OFActionSetDlSrc)a).getDlAddr().toString());
+                break;
+            case SET_DL_DST:
+                sb.append(STR_DL_DST_SET).append("=").append(((OFActionSetDlDst)a).getDlAddr().toString());
+                break;
+            case SET_NW_ECN:
+                sb.append(STR_NW_ECN_SET).append("=").append(Byte.toString(((OFActionSetNwEcn)a).getNwEcn().getEcnValue()));
+                break;
+            case SET_NW_TOS:
+                sb.append(STR_NW_TOS_SET).append("=").append(Short.toString(((OFActionSetNwTos)a).getNwTos()));
+                break;
+            case SET_NW_TTL:
+                sb.append(STR_NW_TTL_SET).append("=").append(Short.toString(((OFActionSetNwTtl)a).getNwTtl()));
+                break;
+            case DEC_NW_TTL:
+                sb.append(STR_NW_TTL_DEC);
+                break;
+            case SET_MPLS_LABEL:
+                sb.append(STR_MPLS_LABEL_SET).append("=").append(Long.toString(((OFActionSetMplsLabel)a).getMplsLabel()));
+                break;
+            case SET_MPLS_TC:
+                sb.append(STR_MPLS_TC_SET).append("=").append(Short.toString(((OFActionSetMplsTc)a).getMplsTc()));
+                break;
+            case SET_MPLS_TTL:
+                sb.append(STR_MPLS_TTL_SET).append("=").append(Short.toString(((OFActionSetMplsTtl)a).getMplsTtl()));
+                break;
+            case DEC_MPLS_TTL:
+                sb.append(STR_MPLS_TTL_DEC);
+                break;
+            case PUSH_MPLS:
+                sb.append(STR_MPLS_PUSH).append("=").append(Integer.toString(((OFActionPushMpls)a).getEthertype().getValue()));
+                break;
+            case POP_MPLS:
+                sb.append(STR_MPLS_POP).append("=").append(Integer.toString(((OFActionPopMpls)a).getEthertype().getValue()));
+                break;
+            case SET_NW_SRC:
+                sb.append(STR_NW_SRC_SET).append("=").append(((OFActionSetNwSrc)a).getNwAddr().toString());
+                break;
+            case SET_NW_DST:
+                sb.append(STR_NW_DST_SET).append("=").append(((OFActionSetNwDst)a).getNwAddr().toString());
+                break;
+            case SET_TP_SRC:
+                sb.append(STR_TP_SRC_SET).append("=").append(((OFActionSetTpSrc)a).getTpPort().toString());
+                break;
+            case SET_TP_DST:
+                sb.append(STR_TP_DST_SET).append("=").append(((OFActionSetTpDst)a).getTpPort().toString());
+                break;
+            case COPY_TTL_IN:
+                sb.append(STR_TTL_IN_COPY);
+                break;
+            case COPY_TTL_OUT:
+                sb.append(STR_TTL_OUT_COPY);
+                break;
+            case PUSH_PBB:
+                sb.append(STR_PBB_PUSH).append("=").append(Integer.toString(((OFActionPushPbb)a).getEthertype().getValue()));
+                break;
+            case POP_PBB:
+                sb.append(STR_PBB_POP);
+                break;
+            case EXPERIMENTER:
+                sb.append(STR_EXPERIMENTER).append("=").append(Long.toString(((OFActionExperimenter)a).getExperimenter()));
+                break;
+            case GROUP:
+                sb.append(STR_GROUP).append("=").append(Integer.toString(((OFActionGroup)a).getGroup().getGroupNumber()));
+                break;
+            case SET_FIELD:
+                log.debug("Got Set-Field action. Setting {}", ((OFActionSetField)a));
+                /* ARP */
+                if (((OFActionSetField)a).getField() instanceof OFOxmArpOp) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_OPCODE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode()));
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSha) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_SHA)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTha) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_DHA)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString());
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSpa) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_SPA)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTpa) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ARP_DPA)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdSll) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_SLL)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTll) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_TLL)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTarget) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_ND_TARGET)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); 
+                }
+                /* DATA LAYER */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmEthType) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_TYPE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue()));
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmEthSrc) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString());
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmEthDst) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanVid) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_VLAN)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanPcp) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_DL_VLAN_PCP)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Byte.toString(((OFOxmVlanPcp) ((OFActionSetField) a).getField()).getValue().getValue())); 
+                } 
+                /* ICMP */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Code) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMP_CODE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Type) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMP_TYPE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Code) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMPV6_CODE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw())); 
+                }  else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Type) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ICMPV6_TYPE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw())); 
+                }
+                /* NETWORK LAYER */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmIpProto) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_PROTO)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Src) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Dst) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Src) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Dst) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Flabel) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_FLOW_LABEL)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Exthdr) {                		
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_IPV6_EXTHDR)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(((OFOxmIpv6Exthdr) ((OFActionSetField) a).getField()).getValue().toString()); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpEcn) { 
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_ECN)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Byte.toString(((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmIpDscp) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_NW_DSCP)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Byte.toString(((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue())); 
+                } 
+                /* TRANSPORT LAYER, TCP, UDP, and SCTP */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmTcpSrc) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpDst) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpSrc) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_UDP_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpDst) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_UDP_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpSrc) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_SCTP_SRC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpDst) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_SCTP_DST)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpFlags) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_TCP_FLAGS)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmTcpFlags) ((OFActionSetField) a).getField()).getValue().getValue())); 
+                }
+                /* MPLS */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmMplsLabel) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_LABEL)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Long.toString(((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsTc) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_TC)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Short.toString(((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue())); 
+                } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsBos) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_MPLS_BOS)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Boolean.toString(((OFOxmMplsBos) ((OFActionSetField) a).getField()).getValue().getValue())); 
+                }
+                /* ACTSET_OUTPUT */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmActsetOutput) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_ACTSET_OUTPUT)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmActsetOutput) ((OFActionSetField) a).getField()).getValue().getPortNumber())); 
+                }
+                /* PACKET_TYPE */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmPacketType) { // TODO hard-coded "/" as delimiter...fix this
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_PACKET_TYPE)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Integer.toString(((OFOxmPacketType) ((OFActionSetField) a).getField()).getValue().getNamespace())) 
+                    .append("/")
+                    .append(Integer.toString(((OFOxmPacketType) ((OFActionSetField) a).getField()).getValue().getNsType())); 
+                }
+                /* METADATA */
+                else if (((OFActionSetField)a).getField() instanceof OFOxmMetadata) {
+                    sb.append(STR_FIELD_SET).append("=").append(MatchUtils.STR_METADATA)
+                    .append(MatchUtils.SET_FIELD_DELIM)
+                    .append(Long.toString(((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue())); 
+                } else {
+                    log.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a));
+                }
+                break;
+            case COPY_FIELD:
+                sb.append(STR_FIELD_COPY).append("=").append(copyFieldToJson((OFActionCopyField) a));
+                break;
+            case METER:
+                sb.append(STR_METER).append("=").append(Long.toString(((OFActionMeter)a).getMeterId()));
+                break;
+            default:
+                log.error("Could not decode action: {}", a);
+                break;
+            }
+
+        }
+
+        return sb.toString();
+    }
+
+    public static List<OFAction> fromString(String s, OFVersion v) {
+        List<OFAction> actions = new LinkedList<OFAction>();
+        OFFactory f = OFFactories.getFactory(v);
+        if (s != null && !s.trim().isEmpty()) {
+            s = s.toLowerCase();
+            String[] bigStringSplit = s.split(","); // split into separate action=value or action=key@value pairs
+
+            String[] tmp;
+            ArrayDeque<String[]> actionToDecode = new ArrayDeque<String[]>();
+            for (int i = 0; i < bigStringSplit.length; i++) {
+                tmp = bigStringSplit[i].split("="); // split into separate [action, value] or [action, key@value] singles
+                if (tmp.length != 2) {
+                    log.debug("Token " + bigStringSplit[i] + " does not have form 'key=value' parsing " + s);
+                }
+                actionToDecode.add(tmp); // actionToDecode contains [key, value] pairs. Create a queue of pairs to process.
+            }	
+
+            while (!actionToDecode.isEmpty()) {
+                String[] keyPair = actionToDecode.pollFirst();
+                String key;
+                String pair;
+                if (keyPair.length != 2) {
+                    log.debug("[Key, Value] {} does not have form 'key=value' parsing, which is okay for some actions e.g. 'pop_vlan'.", keyPair);
+                    key = keyPair[0]; // could the be case of a constant actions (e.g. copy_ttl_in)
+                    pair = "";
+                } else {
+                    key = keyPair[0];
+                    pair = keyPair[1];
+                }
+
+                OFAction a = null;
+                try {
+                    switch (key) {
+                    case STR_OUTPUT:
+                        a = decode_output(pair, v);
+                        break;
+                    case STR_ENQUEUE:
+                        a = decode_enqueue(pair, v);
+                        break;
+                    case STR_DL_SRC_SET:
+                        a = decode_set_src_mac(pair, v);
+                        break;
+                    case STR_DL_DST_SET:
+                        a = decode_set_dst_mac(pair, v);
+                        break;
+                    case STR_EXPERIMENTER:
+                        //no-op. Not implemented
+                        log.error("OFAction EXPERIMENTER not implemented.");
+                        break;
+                    case STR_FIELD_SET: /* ONLY OF1.1+ should get in here. These should only be header fields valid within a set-field. */
+                        String[] actionData = pair.split(MatchUtils.SET_FIELD_DELIM);
+                        if (actionData.length != 2) {
+                            throw new IllegalArgumentException("[Action, Data] " + keyPair + " does not have form 'action=data'" + actionData);
+                        }
 
-	/* OF1.3 set-field operations are defined as any OF1.3 match.
-	 * We will borrow MatchUtils's String definitions of all OF1.3
-	 * set-field operations to be consistent.
-	 */
+                        switch (actionData[0]) {
+                        case MatchUtils.STR_ARP_OPCODE:      
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildArpOp()
+                                    .setValue(ArpOpcode.of(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ARP_SHA:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildArpSha().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ARP_DHA:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildArpTha().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ARP_SPA:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildArpSpa().setValue(IPv4Address.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ARP_DPA:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildArpTpa().setValue(IPv4Address.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_IPV6_ND_SLL:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6NdSll().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_IPV6_ND_TLL:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6NdTll().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_IPV6_ND_TARGET:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6NdTarget().setValue(IPv6Address.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_DL_TYPE:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildEthType()
+                                    .setValue(EthType.of(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_DL_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildEthSrc().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_DL_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildEthDst().setValue(MacAddress.of(actionData[1])).build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_DL_VLAN:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildVlanVid()
+                                    .setValue(OFVlanVidMatch.ofVlan(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_DL_VLAN_PCP:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildVlanPcp()
+                                    .setValue(VlanPcp.of(ParseUtils.parseHexOrDecByte(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ICMP_CODE:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIcmpv4Code()
+                                    .setValue(ICMPv4Code.of(ParseUtils.parseHexOrDecShort(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ICMP_TYPE:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIcmpv4Type()
+                                    .setValue(ICMPv4Type.of(ParseUtils.parseHexOrDecShort(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ICMPV6_CODE:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIcmpv6Code()
+                                    .setValue(U8.of(ParseUtils.parseHexOrDecShort(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ICMPV6_TYPE:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIcmpv6Type()
+                                    .setValue(U8.of(ParseUtils.parseHexOrDecShort(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_NW_PROTO:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpProto()
+                                    .setValue(IpProtocol.of(ParseUtils.parseHexOrDecShort(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_NW_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv4Src().setValue(IPv4Address.of(actionData[1])).build())
+                            .build();						
+                            break;
+                        case MatchUtils.STR_NW_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv4Dst().setValue(IPv4Address.of(actionData[1])).build())
+                            .build();						
+                            break;
+                        case MatchUtils.STR_IPV6_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6Src().setValue(IPv6Address.of(actionData[1])).build())
+                            .build();						
+                            break;
+                        case MatchUtils.STR_IPV6_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6Dst().setValue(IPv6Address.of(actionData[1])).build())
+                            .build();						
+                            break;
+                        case MatchUtils.STR_IPV6_FLOW_LABEL:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpv6Flabel()
+                                    .setValue(IPv6FlowLabel.of(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();			
+                            break;
+                        case MatchUtils.STR_NW_ECN:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpEcn()
+                                    .setValue(IpEcn.of(ParseUtils.parseHexOrDecByte(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_NW_DSCP:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildIpDscp()
+                                    .setValue(IpDscp.of(ParseUtils.parseHexOrDecByte(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_SCTP_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildSctpSrc()
+                                    .setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_SCTP_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildSctpDst()
+                                    .setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_TCP_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildTcpSrc()
+                                    .setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_TCP_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildTcpDst()
+                                    .setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_UDP_SRC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildUdpSrc()
+                                    .setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_UDP_DST:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildUdpDst()
+                                    .setValue(TransportPort.of(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();	
+                            break;
+                        case MatchUtils.STR_MPLS_LABEL:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildMplsLabel()
+                                    .setValue(U32.of(ParseUtils.parseHexOrDecLong(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_MPLS_TC:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildMplsTc()
+                                    .setValue(U8.of(ParseUtils.parseHexOrDecShort(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_MPLS_BOS:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildMplsBos()
+                                    .setValue(OFBooleanValue.of(Boolean.parseBoolean(actionData[1])))
+                                    .build()) // interprets anything other than "true" as false
+                            .build();
+                            break;
+                        case MatchUtils.STR_METADATA:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildMetadata()
+                                    .setValue(OFMetadata.of(U64.of(ParseUtils.parseHexOrDecLong(actionData[1]))))
+                                    .build())
+                            .build();
+                            break;
+                        case MatchUtils.STR_ACTSET_OUTPUT:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildActsetOutput()
+                                    .setValue(portFromString(actionData[1]))
+                                    .build())
+                            .build();
 
-	/**
-	 * Returns a String representation of all the OpenFlow actions.
-	 * @param actions; A list of OFActions to encode into one string
-	 * @return A dpctl-style string of the actions
-	 */
-	public static String actionsToString(List<OFAction> actions, Logger log) {
-		StringBuilder sb = new StringBuilder();
-		for (OFAction a : actions) {
-			if (sb.length() > 0) {
-				sb.append(',');
-			}
-			switch(a.getType()) {
-			case OUTPUT:
-				sb.append(STR_OUTPUT + "=" + Integer.toString(((OFActionOutput)a).getPort().getPortNumber()));
-				break;
-			case ENQUEUE:
-				long queue = ((OFActionEnqueue)a).getQueueId();
-				OFPort port = ((OFActionEnqueue)a).getPort();
-				sb.append(STR_ENQUEUE + "=" + Integer.toString(port.getPortNumber()) + ":0x" + String.format("%02x", queue));
-				break;
-			case STRIP_VLAN:
-				sb.append(STR_VLAN_STRIP);
-				break;
-			case POP_VLAN:
-				sb.append(STR_VLAN_POP);
-				break;
-			case PUSH_VLAN:
-				sb.append(STR_VLAN_PUSH + "=" + Integer.toString(((OFActionPushVlan)a).getEthertype().getValue()));
-				break;
-			case SET_VLAN_VID:
-				sb.append(STR_VLAN_SET_VID + "=" + Short.toString(((OFActionSetVlanVid)a).getVlanVid().getVlan()));
-				break;
-			case SET_VLAN_PCP:
-				sb.append(STR_VLAN_SET_PCP + "=" + Byte.toString(((OFActionSetVlanPcp)a).getVlanPcp().getValue()));
-				break;
-			case SET_QUEUE:
-				sb.append(STR_QUEUE_SET + "=" + Long.toString(((OFActionSetQueue)a).getQueueId()));
-			case SET_DL_SRC:
-				sb.append(STR_DL_SRC_SET + "=" +  ((OFActionSetDlSrc)a).getDlAddr().toString());
-				break;
-			case SET_DL_DST:
-				sb.append(STR_DL_DST_SET + "=" + ((OFActionSetDlDst)a).getDlAddr().toString());
-				break;
-			case SET_NW_ECN:
-				sb.append(STR_NW_ECN_SET + "=" + Byte.toString(((OFActionSetNwEcn)a).getNwEcn().getEcnValue()));
-				break;
-			case SET_NW_TOS:
-				sb.append(STR_NW_TOS_SET + "=" + Short.toString(((OFActionSetNwTos)a).getNwTos()));
-				break;
-			case SET_NW_TTL:
-				sb.append(STR_NW_TTL_SET + "=" + Short.toString(((OFActionSetNwTtl)a).getNwTtl()));
-				break;
-			case DEC_NW_TTL:
-				sb.append(STR_NW_TTL_DEC);
-				break;
-			case SET_MPLS_LABEL:
-				sb.append(STR_MPLS_LABEL_SET + "=" + Long.toString(((OFActionSetMplsLabel)a).getMplsLabel()));
-				break;
-			case SET_MPLS_TC:
-				sb.append(STR_MPLS_TC_SET + "=" + Short.toString(((OFActionSetMplsTc)a).getMplsTc()));
-				break;
-			case SET_MPLS_TTL:
-				sb.append(STR_MPLS_TTL_SET + "=" + Short.toString(((OFActionSetMplsTtl)a).getMplsTtl()));
-				break;
-			case DEC_MPLS_TTL:
-				sb.append(STR_MPLS_TTL_DEC);
-				break;
-			case PUSH_MPLS:
-				sb.append(STR_MPLS_PUSH + "=" + Integer.toString(((OFActionPushMpls)a).getEthertype().getValue()));
-				break;
-			case POP_MPLS:
-				sb.append(STR_MPLS_POP + "=" + Integer.toString(((OFActionPopMpls)a).getEthertype().getValue()));
-				break;
-			case SET_NW_SRC:
-				sb.append(STR_NW_SRC_SET + "=" + ((OFActionSetNwSrc)a).getNwAddr().toString());
-				break;
-			case SET_NW_DST:
-				sb.append(STR_NW_DST_SET + "=" + ((OFActionSetNwDst)a).getNwAddr().toString());
-				break;
-			case SET_TP_SRC:
-				sb.append(STR_TP_SRC_SET + "=" + ((OFActionSetTpSrc)a).getTpPort().toString());
-				break;
-			case SET_TP_DST:
-				sb.append(STR_TP_DST_SET + "=" + ((OFActionSetTpDst)a).getTpPort().toString());
-				break;
-			case COPY_TTL_IN:
-				sb.append(STR_TTL_IN_COPY);
-				break;
-			case COPY_TTL_OUT:
-				sb.append(STR_TTL_OUT_COPY);
-				break;
-			case PUSH_PBB:
-				sb.append(STR_PBB_PUSH + "=" + Integer.toString(((OFActionPushPbb)a).getEthertype().getValue()));
-				break;
-			case POP_PBB:
-				sb.append(STR_PBB_POP);
-				break;
-			case EXPERIMENTER:
-				sb.append(STR_EXPERIMENTER + "=" + Long.toString(((OFActionExperimenter)a).getExperimenter()));
-				break;
-			case GROUP:
-				sb.append(STR_GROUP + "=" + Integer.toString(((OFActionGroup)a).getGroup().getGroupNumber()));
-				break;
-			case SET_FIELD:
-				log.debug("Got Set-Field action. Setting " + ((OFActionSetField)a));
-				/* ARP */
-				if (((OFActionSetField)a).getField() instanceof OFOxmArpOp) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_ARP_OPCODE + MatchUtils.SET_FIELD_DELIM + Integer.toString(((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode()));
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmArpSha) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_ARP_SHA + MatchUtils.SET_FIELD_DELIM + ((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmArpTha) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_ARP_DHA + MatchUtils.SET_FIELD_DELIM + ((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString());
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmArpSpa) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_ARP_SPA + MatchUtils.SET_FIELD_DELIM + ((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmArpTpa) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_ARP_DPA + MatchUtils.SET_FIELD_DELIM + ((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdSll) {                		
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_IPV6_ND_SSL + MatchUtils.SET_FIELD_DELIM + ((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTll) {                		
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_IPV6_ND_TTL + MatchUtils.SET_FIELD_DELIM + ((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTarget) {                		
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_IPV6_ND_TARGET + MatchUtils.SET_FIELD_DELIM + ((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); 
-				}
-				/* DATA LAYER */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmEthType) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_DL_TYPE + MatchUtils.SET_FIELD_DELIM + Integer.toString(((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue()));
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmEthSrc) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_DL_SRC + MatchUtils.SET_FIELD_DELIM + ((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString());
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmEthDst) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_DL_DST + MatchUtils.SET_FIELD_DELIM + ((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmVlanVid) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_DL_VLAN + MatchUtils.SET_FIELD_DELIM + Short.toString(((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmVlanPcp) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_DL_VLAN_PCP + MatchUtils.SET_FIELD_DELIM + Byte.toString(((OFOxmVlanPcp) ((OFActionSetField) a).getField()).getValue().getValue())); 
-				} 
-				/* ICMP */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Code) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_ICMP_CODE + MatchUtils.SET_FIELD_DELIM + Short.toString(((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Type) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_ICMP_TYPE + MatchUtils.SET_FIELD_DELIM + Short.toString(((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Code) {                		
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_ICMPV6_CODE + MatchUtils.SET_FIELD_DELIM + Short.toString(((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw())); 
-				}  else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Type) {                		
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_ICMPV6_TYPE + MatchUtils.SET_FIELD_DELIM + Short.toString(((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw())); 
-				}
-				/* NETWORK LAYER */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmIpProto) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_NW_PROTO + MatchUtils.SET_FIELD_DELIM + Short.toString(((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Src) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_NW_SRC + MatchUtils.SET_FIELD_DELIM + ((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Dst) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_NW_DST + MatchUtils.SET_FIELD_DELIM + ((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Src) {                		
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_IPV6_SRC + MatchUtils.SET_FIELD_DELIM + ((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Dst) {                		
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_IPV6_DST + MatchUtils.SET_FIELD_DELIM + ((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Flabel) {                		
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_IPV6_FLOW_LABEL + MatchUtils.SET_FIELD_DELIM + ((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpEcn) { //TODO @Ryan ECN and DSCP need to have their own columns for OF1.3....
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_NW_ECN + MatchUtils.SET_FIELD_DELIM + Byte.toString(((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmIpDscp) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_NW_DSCP + MatchUtils.SET_FIELD_DELIM + Byte.toString(((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue())); 
-				} 
-				/* TRANSPORT LAYER, TCP, UDP, and SCTP */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmTcpSrc) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_TCP_SRC + MatchUtils.SET_FIELD_DELIM + Integer.toString(((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmTcpDst) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_TCP_DST + MatchUtils.SET_FIELD_DELIM + Integer.toString(((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmUdpSrc) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_UDP_SRC + MatchUtils.SET_FIELD_DELIM + Integer.toString(((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmUdpDst) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_UDP_DST + MatchUtils.SET_FIELD_DELIM + Integer.toString(((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmSctpSrc) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_SCTP_SRC + MatchUtils.SET_FIELD_DELIM + Integer.toString(((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmSctpDst) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_SCTP_DST + MatchUtils.SET_FIELD_DELIM + Integer.toString(((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort())); 
-				}
-				/* MPLS */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmMplsLabel) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_MPLS_LABEL + MatchUtils.SET_FIELD_DELIM + Long.toString(((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue())); 
-				} else if (((OFActionSetField)a).getField() instanceof OFOxmMplsTc) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_MPLS_TC + MatchUtils.SET_FIELD_DELIM + Short.toString(((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue())); 
-				} // MPLS_BOS not implemented in loxi
-				/* METADATA */
-				else if (((OFActionSetField)a).getField() instanceof OFOxmMetadata) {
-					sb.append(STR_FIELD_SET + "=" + MatchUtils.STR_METADATA + MatchUtils.SET_FIELD_DELIM + Long.toString(((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue())); 
-				} else {
-					log.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a));
-				}
-				break;
-			default:
-				log.error("Could not decode action: {}", a);
-				break;
-			}
+                            break;
+                        case MatchUtils.STR_TCP_FLAGS:
+                            a = f.actions().buildSetField()
+                            .setField(f.oxms().buildTcpFlags()
+                                    .setValue(U16.of(ParseUtils.parseHexOrDecInt(actionData[1])))
+                                    .build())
+                            .build();
+                            break;
+                        default:
+                            log.error("Unexpected OF1.2+ setfield '{}'", actionData);
+                            break;
+                        }					
+                        break;
+                    case STR_GROUP:
+                        a = f.actions().buildGroup()
+                        .setGroup(GroupUtils.groupIdFromString(pair))
+                        .build();	
+                        break;
+                    case STR_MPLS_LABEL_SET:
+                        a = f.actions().buildSetMplsLabel()
+                        .setMplsLabel(ParseUtils.parseHexOrDecLong(pair))
+                        .build();			
+                        break;
+                    case STR_MPLS_POP:
+                        a = f.actions().buildPopMpls()
+                        .setEthertype(EthType.of(ParseUtils.parseHexOrDecInt(pair)))
+                        .build();
+                        break;
+                    case STR_MPLS_PUSH:
+                        a = f.actions().buildPushMpls()
+                        .setEthertype(EthType.of(ParseUtils.parseHexOrDecInt(pair)))
+                        .build();		
+                        break;
+                    case STR_MPLS_TC_SET:
+                        a = f.actions().buildSetMplsTc()
+                        .setMplsTc(ParseUtils.parseHexOrDecShort(pair))
+                        .build();	
+                        break;
+                    case STR_MPLS_TTL_DEC:
+                        a = f.actions().decMplsTtl();
+                        break;
+                    case STR_MPLS_TTL_SET:
+                        a = f.actions().buildSetMplsTtl()
+                        .setMplsTtl(ParseUtils.parseHexOrDecShort(pair))
+                        .build();	
+                        break;
+                    case STR_NW_TOS_SET:
+                        a = decode_set_tos_bits(pair, v); // should only be used by OF1.0
+                        break;
+                    case STR_NW_SRC_SET:
+                        a = decode_set_src_ip(pair, v);
+                        break;
+                    case STR_NW_DST_SET:
+                        a = decode_set_dst_ip(pair, v);
+                        break;
+                    case STR_NW_ECN_SET: // loxi does not support DSCP set for OF1.1
+                        a = f.actions().buildSetNwEcn() 
+                        .setNwEcn(IpEcn.of(ParseUtils.parseHexOrDecByte(pair)))
+                        .build();		
+                        break;
+                    case STR_NW_TTL_DEC:
+                        a = f.actions().decNwTtl();
+                        break;
+                    case STR_NW_TTL_SET:
+                        a = f.actions().buildSetNwTtl()
+                        .setNwTtl(ParseUtils.parseHexOrDecShort(pair))
+                        .build();
+                        break;
+                    case STR_PBB_POP:
+                        a = f.actions().popPbb();
+                        break;
+                    case STR_PBB_PUSH:
+                        a = f.actions().buildPushPbb()
+                        .setEthertype(EthType.of(ParseUtils.parseHexOrDecInt(pair)))
+                        .build();				
+                        break;
+                    case STR_QUEUE_SET:
+                        a = f.actions().buildSetQueue()
+                        .setQueueId(ParseUtils.parseHexOrDecLong(pair))
+                        .build();	
+                        break;
+                    case STR_TP_SRC_SET:
+                        a = decode_set_src_port(pair, v);
+                        break;
+                    case STR_TP_DST_SET:
+                        a = decode_set_dst_port(pair, v);
+                        break;
+                    case STR_TTL_IN_COPY:
+                        a = f.actions().copyTtlIn();
+                        break;
+                    case STR_TTL_OUT_COPY:
+                        a = f.actions().copyTtlOut();
+                        break;
+                    case STR_VLAN_POP:
+                        a = f.actions().popVlan();
+                        break;
+                    case STR_VLAN_PUSH:
+                        a = f.actions().buildPushVlan()
+                        .setEthertype(EthType.of(ParseUtils.parseHexOrDecInt(pair)))
+                        .build();
+                        break;
+                    case STR_VLAN_STRIP:
+                        a = f.actions().stripVlan();
+                        break;
+                    case STR_VLAN_SET_VID:
+                        a = decode_set_vlan_id(pair, v);
+                        break;
+                    case STR_VLAN_SET_PCP:
+                        a = decode_set_vlan_priority(pair, v);
+                        break;
+                    case STR_METER:
+                        a = f.actions().buildMeter()
+                        .setMeterId(ParseUtils.parseHexOrDecLong(pair))
+                        .build();
+                        break;
+                    case STR_FIELD_COPY:
+                        a = (OFAction) copyFieldFromJson(pair, f.getVersion());
+                        break;
+                    default:
+                        log.error("Unexpected action key '{}'", keyPair);
+                        break;
+                    }
 
-		}
+                } catch (Exception e) {
+                    log.error("Illegal Action: {}", e.getMessage());
+                }
+                if (a != null) {
+                    actions.add(a);
+                }
+            }
+        } else {
+            log.debug("actions not found --> drop");
+        }	
+        return actions;
+    }
 
-		return sb.toString();
-	}
+    /**
+     * Append OFActionCopyField object to an existing JsonGenerator.
+     * This method assumes the field name of the action has been
+     * written already, if required. The appended data will
+     * be formatted as follows:
+     *   {
+     *       "src_field":"name",
+     *       "dst_field":"name",
+     *       "src_offset_bits":"bits",
+     *       "dst_offset_bits":"bits",
+     *       "num_bits":"bits"
+     *   }
+     * @param jsonGen
+     * @param c
+     */
+    public static void copyFieldToJson(JsonGenerator jsonGen, OFActionCopyField c) {
+        jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);
 
-	/**
-	 * Parses OFFlowMod actions from strings.
-	 * @param fmb The OFFlowMod.Builder to set the actions for
-	 * @param bigString The string containing all the actions
-	 * @param log A logger to log for errors.
-	 */
-	public static void fromString(OFFlowMod.Builder fmb, String bigString, Logger log) {
-		List<OFAction> actions = new LinkedList<OFAction>();
-		if (bigString != null && !bigString.trim().isEmpty()) {
-			bigString = bigString.toLowerCase();
-			String[] bigStringSplit = bigString.split(","); // split into separate action=value or action=key@value pairs
+        try {
+            jsonGen.writeStartObject();
+            Iterator<OFOxm<?>> i = c.getOxmIds().iterator();
+            if (i.hasNext()) {
+                jsonGen.writeStringField("src_field" , OXMUtils.oxmIdToString(U32.of(i.next().getCanonical().getTypeLen())));
+            } else {
+                log.error("either src_field or dst_field or both not set in {}", c);
+            }
+            if (i.hasNext()) {
+                jsonGen.writeStringField("dst_field" , OXMUtils.oxmIdToString(U32.of(i.next().getCanonical().getTypeLen())));
+            } else {
+                log.error("either src_field or dst_field not set in {}", c);
+            }
+            if (i.hasNext()) {
+                log.warn("OFOxmList should only have src_field followed by dst_field. Extra field {}", i.next());
+            }
+            jsonGen.writeNumberField("src_offset_bits", c.getSrcOffset());
+            jsonGen.writeNumberField("dst_offset_bits", c.getDstOffset());
+            jsonGen.writeNumberField("num_bits", c.getNBits());
+            jsonGen.writeEndObject();
+            jsonGen.close();
+        } catch (IOException e) {
+            log.error("Error composing OFActionCopyField JSON object. {}", e.getMessage());
+            return;
+        }
+    }
 
-			String[] tmp;
-			ArrayDeque<String[]> actionToDecode = new ArrayDeque<String[]>();
-			for (int i = 0; i < bigStringSplit.length; i++) {
-				tmp = bigStringSplit[i].split("="); // split into separate [action, value] or [action, key@value] singles
-				if (tmp.length != 2) {
-					log.debug("Token " + bigStringSplit[i] + " does not have form 'key=value' parsing " + bigString);
-				}
-				actionToDecode.add(tmp); // actionToDecode contains [key, value] pairs. Create a queue of pairs to process.
-			}	
+    /**
+     * Convert OFActionCopyField object to a JSON string.
+     * This method assumes the field name of the action has been
+     * written already, if required. The appended data will
+     * be formatted as follows:
+     *   {
+     *       "src_field":"name",
+     *       "dst_field":"name",
+     *       "src_offset_bits":"bits",
+     *       "dst_offset_bits":"bits",
+     *       "num_bits":"bits"
+     *   }
+     * @param jsonGen
+     * @param c
+     */
+    public static String copyFieldToJson(OFActionCopyField c) {
+        Writer w = new StringWriter();
+        JsonGenerator jsonGen;
+        try {
+            jsonGen = jsonFactory.createGenerator(w);
+        } catch (IOException e) {
+            log.error("Could not instantiate JSON Generator. {}", e.getMessage());
+            return JSON_EMPTY_OBJECT;
+        }
 
-			while (!actionToDecode.isEmpty()) {
-				String[] keyPair = actionToDecode.pollFirst();
-				String key;
-				String pair;
-				if (keyPair.length != 2) {
-					log.debug("[Key, Value] {} does not have form 'key=value' parsing, which is okay for some actions e.g. 'pop_vlan'.", keyPair);
-					key = keyPair[0]; // could the be case of a constant actions (e.g. copy_ttl_in)
-					pair = "";
-				} else {
-					key = keyPair[0];
-					pair = keyPair[1];
-				}
+        copyFieldToJson(jsonGen, c);
 
-				OFAction a = null;
-				try {
-					switch (key) {
-					case STR_OUTPUT:
-						a = decode_output(pair, fmb.getVersion(), log);
-						break;
-					case STR_ENQUEUE:
-						a = decode_enqueue(pair, fmb.getVersion(), log);
-						break;
-					case STR_DL_SRC_SET:
-						a = decode_set_src_mac(pair, fmb.getVersion(), log);
-						break;
-					case STR_DL_DST_SET:
-						a = decode_set_dst_mac(pair, fmb.getVersion(), log);
-						break;
-					case STR_EXPERIMENTER:
-						//no-op. Not implemented
-						log.error("OFAction EXPERIMENTER not implemented.");
-						break;
-					case STR_FIELD_SET: /* ONLY OF1.1+ should get in here. These should only be header fields valid within a set-field. */
-						String[] actionData = pair.split(MatchUtils.SET_FIELD_DELIM);
-						if (actionData.length != 2) {
-							throw new IllegalArgumentException("[Action, Data] " + keyPair + " does not have form 'action=data'" + actionData);
-						}
+        return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */
+    }
 
-						switch (actionData[0]) {
-						case MatchUtils.STR_ARP_OPCODE:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildArpOp().setValue(ArpOpcode.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildArpOp().setValue(ArpOpcode.of(Integer.parseInt(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ARP_SHA:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildArpSha().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_ARP_DHA:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildArpTha().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_ARP_SPA:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildArpSpa().setValue(IPv4Address.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_ARP_DPA:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildArpTpa().setValue(IPv4Address.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_IPV6_ND_SSL:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpv6NdSll().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_IPV6_ND_TTL:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpv6NdTll().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_IPV6_ND_TARGET:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpv6NdTarget().setValue(IPv6Address.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_DL_TYPE:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildEthType().setValue(EthType.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildEthType().setValue(EthType.of(Integer.parseInt(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_DL_SRC:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildEthSrc().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_DL_DST:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildEthDst().setValue(MacAddress.of(actionData[1])).build())
-							.build();
-							break;
-						case MatchUtils.STR_DL_VLAN:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildVlanVid().setValue(OFVlanVidMatch.ofVlan(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildVlanVid().setValue(OFVlanVidMatch.ofVlan(Integer.parseInt(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_DL_VLAN_PCP:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildVlanPcp().setValue(VlanPcp.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildVlanPcp().setValue(VlanPcp.of(Byte.parseByte(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ICMP_CODE:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIcmpv4Code().setValue(ICMPv4Code.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIcmpv4Code().setValue(ICMPv4Code.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ICMP_TYPE:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIcmpv4Type().setValue(ICMPv4Type.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIcmpv4Type().setValue(ICMPv4Type.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ICMPV6_CODE:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIcmpv6Code().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIcmpv6Code().setValue(U8.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_ICMPV6_TYPE:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIcmpv6Type().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIcmpv6Type().setValue(U8.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_NW_PROTO:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpProto().setValue(IpProtocol.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpProto().setValue(IpProtocol.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_NW_SRC:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpv4Src().setValue(IPv4Address.of(actionData[1])).build())
-							.build();						
-							break;
-						case MatchUtils.STR_NW_DST:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpv4Dst().setValue(IPv4Address.of(actionData[1])).build())
-							.build();						
-							break;
-						case MatchUtils.STR_IPV6_SRC:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpv6Src().setValue(IPv6Address.of(actionData[1])).build())
-							.build();						
-							break;
-						case MatchUtils.STR_IPV6_DST:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpv6Dst().setValue(IPv6Address.of(actionData[1])).build())
-							.build();						
-							break;
-						case MatchUtils.STR_IPV6_FLOW_LABEL:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpv6Flabel().setValue(IPv6FlowLabel.of(Integer.parseInt(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();			
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpv6Flabel().setValue(IPv6FlowLabel.of(Integer.parseInt(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_NW_ECN:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpEcn().setValue(IpEcn.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpEcn().setValue(IpEcn.of(Byte.parseByte(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_NW_DSCP:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpDscp().setValue(IpDscp.of(Byte.parseByte(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildIpDscp().setValue(IpDscp.of(Byte.parseByte(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_SCTP_SRC:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildSctpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_SCTP_DST:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildSctpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_TCP_SRC:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildTcpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_TCP_DST:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildTcpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_UDP_SRC:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildUdpSrc().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_UDP_DST:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildUdpDst().setValue(TransportPort.of(Integer.parseInt(actionData[1]))).build())
-							.build();	
-							break;
-						case MatchUtils.STR_MPLS_LABEL:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildMplsLabel().setValue(U32.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildMplsLabel().setValue(U32.of(Long.parseLong(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_MPLS_TC:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildMplsTc().setValue(U8.of(Short.parseShort(actionData[1].replaceFirst("0x", ""), 16))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildMplsTc().setValue(U8.of(Short.parseShort(actionData[1]))).build())
-										.build();
-							}
-							break;
-						case MatchUtils.STR_MPLS_BOS:
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-							.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildMplsBos().setValue(OFBooleanValue.of(Boolean.parseBoolean(actionData[1]))).build()) // interprets anything other than "true" as false
-							.build();
-							break;
-						case MatchUtils.STR_METADATA:
-							if (actionData[1].startsWith("0x")) {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildMetadata().setValue(OFMetadata.of(U64.of(Long.parseLong(actionData[1].replaceFirst("0x", ""), 16)))).build())
-										.build();
-							} else {
-								a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetField()
-										.setField(OFFactories.getFactory(fmb.getVersion()).oxms().buildMetadata().setValue(OFMetadata.of(U64.of(Long.parseLong(actionData[1])))).build())
-										.build();
-							}
-							break;
-						default:
-							log.error("UNEXPECTED OF1.2+ SET-FIELD '{}'", actionData);
-							break;
-						}					
-						break;
-					case STR_GROUP:
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildGroup()
-									.setGroup(OFGroup.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
-									.build();	
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildGroup()
-									.setGroup(OFGroup.of(Integer.parseInt(pair)))
-									.build();		
-						}
-						break;
-					case STR_MPLS_LABEL_SET:
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetMplsLabel()
-									.setMplsLabel(Long.parseLong(pair.replaceFirst("0x", ""), 16))
-									.build();			
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetMplsLabel()
-									.setMplsLabel(Long.parseLong(pair))
-									.build();					
-						}
-						break;
-					case STR_MPLS_POP:
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildPopMpls()
-									.setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
-									.build();
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildPopMpls()
-									.setEthertype(EthType.of(Integer.parseInt(pair)))
-									.build();	
-						}
-						break;
-					case STR_MPLS_PUSH:
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildPushMpls()
-									.setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
-									.build();		
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildPushMpls()
-									.setEthertype(EthType.of(Integer.parseInt(pair)))
-									.build();			
-						}
-						break;
-					case STR_MPLS_TC_SET:
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetMplsTc()
-									.setMplsTc(Short.parseShort(pair.replaceFirst("0x", ""), 16))
-									.build();	
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetMplsTc()
-									.setMplsTc(Short.parseShort(pair))
-									.build();			
-						}
-						break;
-					case STR_MPLS_TTL_DEC:
-						a = OFFactories.getFactory(fmb.getVersion()).actions().decMplsTtl();
-						break;
-					case STR_MPLS_TTL_SET:
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetMplsTtl()
-									.setMplsTtl(Short.parseShort(pair.replaceFirst("0x", ""), 16))
-									.build();	
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetMplsTtl()
-									.setMplsTtl(Short.parseShort(pair))
-									.build();				
-						}
-						break;
-					case STR_NW_TOS_SET:
-						a = decode_set_tos_bits(pair, fmb.getVersion(), log); // should only be used by OF1.0
-						break;
-					case STR_NW_SRC_SET:
-						a = decode_set_src_ip(pair, fmb.getVersion(), log);
-						break;
-					case STR_NW_DST_SET:
-						a = decode_set_dst_ip(pair, fmb.getVersion(), log);
-						break;
-					case STR_NW_ECN_SET: // loxi does not support DSCP set for OF1.1
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetNwEcn()
-									.setNwEcn(IpEcn.of(Byte.parseByte(pair.replaceFirst("0x", ""), 16)))
-									.build();		
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetNwEcn()
-									.setNwEcn(IpEcn.of(Byte.parseByte(pair)))
-									.build();							
-						}
-						break;
-					case STR_NW_TTL_DEC:
-						a = OFFactories.getFactory(fmb.getVersion()).actions().decNwTtl();
-						break;
-					case STR_NW_TTL_SET:
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetNwTtl()
-									.setNwTtl(Short.parseShort(pair.replaceFirst("0x", ""), 16))
-									.build();
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetNwTtl()
-									.setNwTtl(Short.parseShort(pair))
-									.build();						
-						}
-						break;
-					case STR_PBB_POP:
-						a = OFFactories.getFactory(fmb.getVersion()).actions().popPbb();
-						break;
-					case STR_PBB_PUSH:
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildPushPbb()
-									.setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
-									.build();				
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildPushPbb()
-									.setEthertype(EthType.of(Integer.parseInt(pair)))
-									.build();					
-						}
-						break;
-					case STR_QUEUE_SET:
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetQueue()
-									.setQueueId(Long.parseLong(pair.replaceFirst("0x", ""), 16))
-									.build();	
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildSetQueue()
-									.setQueueId(Long.parseLong(pair))
-									.build();					
-						}
-						break;
-					case STR_TP_SRC_SET:
-						a = decode_set_src_port(pair, fmb.getVersion(), log);
-						break;
-					case STR_TP_DST_SET:
-						a = decode_set_dst_port(pair, fmb.getVersion(), log);
-						break;
-					case STR_TTL_IN_COPY:
-						a = OFFactories.getFactory(fmb.getVersion()).actions().copyTtlIn();
-						break;
-					case STR_TTL_OUT_COPY:
-						a = OFFactories.getFactory(fmb.getVersion()).actions().copyTtlOut();
-						break;
-					case STR_VLAN_POP:
-						a = OFFactories.getFactory(fmb.getVersion()).actions().popVlan();
-						break;
-					case STR_VLAN_PUSH:
-						if (pair.startsWith("0x")) {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildPushVlan()
-									.setEthertype(EthType.of(Integer.parseInt(pair.replaceFirst("0x", ""), 16)))
-									.build();
-						} else {
-							a = OFFactories.getFactory(fmb.getVersion()).actions().buildPushVlan()
-									.setEthertype(EthType.of(Integer.parseInt(pair)))
-									.build();		
-						}
-						break;
-					case STR_VLAN_STRIP:
-						a = OFFactories.getFactory(fmb.getVersion()).actions().stripVlan();
-						break;
-					case STR_VLAN_SET_VID:
-						a = decode_set_vlan_id(pair, fmb.getVersion(), log);
-						break;
-					case STR_VLAN_SET_PCP:
-						a = decode_set_vlan_priority(pair, fmb.getVersion(), log);
-						break;
-					default:
-						log.error("UNEXPECTED ACTION KEY '{}'", keyPair);
-						break;
-					}
+    /**
+     * Convert a JSON string to an OFActionCopyField object.
+     * The format of the input JSON is expected to be:
+     *   {
+     *       "src_field":"name",
+     *       "dst_field":"name",
+     *       "src_offset_bits":"bits",
+     *       "dst_offset_bits":"bits",
+     *       "num_bits":"bits"
+     *   }
+     * @param json
+     * @param v
+     * @return
+     */
+    public static OFActionCopyField copyFieldFromJson(String json, OFVersion v) {
+        if (json == null) {
+            throw new IllegalArgumentException("JSON string cannot be null");
+        }
+        if (v == null) {
+            throw new IllegalArgumentException("OFVersion cannot be null");
+        }
 
-				} catch (Exception e) {
-					log.error("Illegal Action: " + e.getMessage());
-				}
-				if (a != null) {
-					actions.add(a);
-				}
-			}
-			log.debug("actions: {}", actions);
-			fmb.setActions(actions);
-		} else {
-			log.debug("actions not found --> drop");
-		}		
-		return;
-	} 
-	
-	/**
-	 * Parse string and numerical port representations.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. Data can be any signed integer
-	 * or hex (w/leading 0x prefix) as a string or the special string port
-	 * STR_PORT_* as defined in {@link MatchUtils}.
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFActionOutput decode_output(String actionToDecode, OFVersion version, Logger log) {
-		OFActionOutput.Builder ab = OFFactories.getFactory(version).actions().buildOutput();
-		actionToDecode = actionToDecode.trim().toLowerCase();
-		OFPort port = OFPort.ZERO;
-		switch (actionToDecode) {
-		case MatchUtils.STR_PORT_ALL:
-			port = OFPort.ALL;
-			break;
-		case MatchUtils.STR_PORT_CONTROLLER:
-			port = OFPort.CONTROLLER;
-			break;
-		case MatchUtils.STR_PORT_FLOOD:
-			port = OFPort.FLOOD;
-			break;
-		case MatchUtils.STR_PORT_IN_PORT:
-			port = OFPort.IN_PORT;
-			break;
-		case MatchUtils.STR_PORT_LOCAL:
-			port = OFPort.LOCAL;
-			break;
-		case MatchUtils.STR_PORT_NORMAL:
-			port = OFPort.NORMAL;
-			break;
-		case MatchUtils.STR_PORT_TABLE:
-			port = OFPort.TABLE;
-			break;
-		default:
-			log.debug("Port {} was not a special port string. Parsing as raw int or hex", actionToDecode);
-		}
-		if (!port.equals(OFPort.ZERO)) {
-			ab.setPort(port);
-			ab.setMaxLen(Integer.MAX_VALUE);
-			log.debug("action {}", ab.build());
-			return ab.build();
-		} else {
-			try {
-				port = OFPort.of(U32.of(actionToDecode.contains("0x") ? 
-						Long.parseLong(actionToDecode.replaceFirst("0x", ""), 16) : 
-							Long.parseLong(actionToDecode)).getRaw());
-				ab.setPort(port);
-				ab.setMaxLen(Integer.MAX_VALUE);
-				return ab.build();
-			} catch (NumberFormatException e) {
-				log.error("Could not parse port: '{}'", actionToDecode);
-				return null;
-			}
-		}
-	}
+        final JsonParser jp;
+        try {
+            jp = jsonFactory.createParser(json);
+        } catch (IOException e) {
+            log.error("Could not create JSON parser for OFActionCopyField {}", json);
+            return null;
+        }
+        try {
+            if (jp.nextToken() != JsonToken.START_OBJECT) {
+                throw new IOException("Expected START_OBJECT");
+            }
 
-	/**
-	 * Parse enqueue actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. Data with a leading 0x is permitted.
-	 *
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFActionEnqueue decode_enqueue(String actionToDecode, OFVersion version, Logger log) {
-		Matcher n = Pattern.compile("(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(actionToDecode);
-		if (n.matches()) {
-			OFPort port = OFPort.of(0);
-			if (n.group(1) != null) {
-				try {
-					port = OFPort.of(get_short(n.group(1)));
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid port-num in: '{}' (error ignored)", actionToDecode);
-					return null;
-				}
-			}
+            OFActionCopyField.Builder b = OFFactories.getFactory(v).buildActionCopyField();
+            OFOxm<?> srcField = null;
+            OFOxm<?> dstField = null;
 
-			int queueid = 0;
-			if (n.group(2) != null) {
-				try {
-					queueid = get_int(n.group(2));
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid queue-id in: '{}' (error ignored)", actionToDecode);
-					return null;
-				}
-			}
-			OFActionEnqueue.Builder ab = OFFactories.getFactory(version).actions().buildEnqueue();
-			ab.setPort(port);
-			ab.setQueueId(queueid);
-			log.debug("action {}", ab.build());
-			return ab.build();
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-	}
+            while (jp.nextToken() != JsonToken.END_OBJECT) {
+                String key = jp.getCurrentName().toLowerCase().trim();
+                jp.nextToken();
+                String value = jp.getText().toLowerCase().trim();
+                switch (key) {
+                case "src_field":
+                    srcField = OXMUtils.oxmStringToOxm(value, v);
+                    break;
+                case "dst_field":
+                    dstField = OXMUtils.oxmStringToOxm(value, v);
+                    break;
+                case "src_offset_bits":
+                    b.setSrcOffset(Integer.parseInt(value));
+                    break;
+                case "dst_offset_bits":
+                    b.setDstOffset(Integer.parseInt(value));
+                    break;
+                case "num_bits":
+                    b.setNBits(Integer.parseInt(value));
+                    break;
+                default:
+                    log.warn("Unexpected OFActionCopyField key {}", key);
+                    break;
+                }
+            }
+            if (srcField == null || dstField == null) {
+                log.error("Src and dst OXMs must be specified. Got {} and {}, respectively", srcField, dstField);
+                return null;
+            } else {
+                b.setOxmIds(OFOxmList.of(srcField, dstField));
+                return b.build();
+            }
+        } catch (IOException e) {
+            log.error("Could not parse: {}", json);
+            log.error("JSON parse error message: {}", e.getMessage());
+            return null;
+        }
+    }
 
-	/**
-	 * Parse set_vlan_id actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. Data with a leading 0x is permitted.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFActionSetVlanVid decode_set_vlan_id(String actionToDecode, OFVersion version, Logger log) {
-		Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode);
-		if (n.matches()) {            
-			if (n.group(1) != null) {
-				try {
-					VlanVid vlanid = VlanVid.ofVlan(get_short(n.group(1)));
-					OFActionSetVlanVid.Builder ab = OFFactories.getFactory(version).actions().buildSetVlanVid();
-					ab.setVlanVid(vlanid);
-					log.debug("action {}", ab.build());
-					return ab.build();
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid VLAN in: {} (error ignored)", actionToDecode);
-					return null;
-				}
-			}          
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-		return null;
-	}
+    /**
+     * Parses OFFlowMod actions from strings.
+     * @param fmb The OFFlowMod.Builder to set the actions for
+     * @param s The string containing all the actions
+     * @param log A logger to log for errors.
+     */
+    public static void fromString(OFFlowMod.Builder fmb, String s) {
+        List<OFAction> actions = fromString(s, fmb.getVersion());
+        log.debug("actions: {}", actions);
+        fmb.setActions(actions);
+        return;
+    }
 
-	/**
-	 * Parse set_vlan_pcp actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. Data with a leading 0x is permitted.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFActionSetVlanPcp decode_set_vlan_priority(String actionToDecode, OFVersion version, Logger log) {
-		Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); 
-		if (n.matches()) {            
-			if (n.group(1) != null) {
-				try {
-					VlanPcp prior = VlanPcp.of(get_byte(n.group(1)));
-					OFActionSetVlanPcp.Builder ab = OFFactories.getFactory(version).actions().buildSetVlanPcp();
-					ab.setVlanPcp(prior);
-					log.debug("action {}", ab.build());
-					return ab.build();
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid VLAN priority in: {} (error ignored)", actionToDecode);
-					return null;
-				}
-			}
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-		return null;
-	}
+    public static OFPort portFromString(String s) {
+        return MatchUtils.portFromString(s);
+    }
 
-	/**
-	 * Parse set_dl_src actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder.
-	 * 
-	 * TODO should consider using MacAddress's built-in parser....
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFActionSetDlSrc decode_set_src_mac(String actionToDecode, OFVersion version, Logger log) {
-		Matcher n = Pattern.compile("(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(actionToDecode); 
-		if (n.matches()) {
-			MacAddress macaddr = MacAddress.of(get_mac_addr(n, actionToDecode, log));
-			if (macaddr != null) {
-				OFActionSetDlSrc.Builder ab = OFFactories.getFactory(version).actions().buildSetDlSrc();
-				ab.setDlAddr(macaddr);
-				log.debug("action {}", ab.build());
-				return ab.build();
-			}            
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-		return null;
-	}
+    public static String portToString(OFPort p) {
+        return MatchUtils.portToString(p);
+    }
 
-	/**
-	 * Parse set_dl_dst actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder.
-	 * 
-	 * TODO should consider using MacAddress's built-in parser....
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFActionSetDlDst decode_set_dst_mac(String actionToDecode, OFVersion version, Logger log) {
-		Matcher n = Pattern.compile("(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(actionToDecode);
-		if (n.matches()) {
-			MacAddress macaddr = MacAddress.of(get_mac_addr(n, actionToDecode, log));            
-			if (macaddr != null) {
-				OFActionSetDlDst.Builder ab = OFFactories.getFactory(version).actions().buildSetDlDst();
-				ab.setDlAddr(macaddr);
-				log.debug("action {}", ab.build());
-				return ab.build();
-			}
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-		return null;
-	}
+    /**
+     * Parse string and numerical port representations.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. Data can be any signed integer
+     * or hex (w/leading 0x prefix) as a string or the special string port
+     * STR_PORT_* as defined in {@link MatchUtils}.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionOutput decode_output(String actionToDecode, OFVersion version) {
+        OFActionOutput.Builder ab = OFFactories.getFactory(version).actions().buildOutput();
+        OFPort port = portFromString(actionToDecode);
+        if (port == null) {
+            log.error("Could not parse output port {}", actionToDecode);
+            return null;
+        } else {
+            ab.setPort(port);
+            ab.setMaxLen(Integer.MAX_VALUE);
+            log.debug("action {}", ab);
+            return ab.build();
+        } 
+    }
 
-	/**
-	 * Parse set_tos actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. A leading 0x is permitted.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFActionSetNwTos decode_set_tos_bits(String actionToDecode, OFVersion version, Logger log) {
-		Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); 
-		if (n.matches()) {
-			if (n.group(1) != null) {
-				try {
-					byte tosbits = get_byte(n.group(1));
-					OFActionSetNwTos.Builder ab = OFFactories.getFactory(version).actions().buildSetNwTos();
-					ab.setNwTos(tosbits);
-					log.debug("action {}", ab.build());
-					return ab.build();
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid dst-port in: {} (error ignored)", actionToDecode);
-					return null;
-				}
-			}
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-		return null;
-	}
+    /**
+     * Parse enqueue actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. Data with a leading 0x is permitted.
+     *
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionEnqueue decode_enqueue(String actionToDecode, OFVersion version) {
+        Matcher n = Pattern.compile("(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(actionToDecode);
+        if (n.matches()) {
+            OFPort port;
+            if (n.group(1) != null) {
+                port = portFromString(n.group(1));
+                if (port == null) {
+                    log.error("Invalid port {}", n.group(1));
+                    return null;
+                }
+            } else {
+                log.error("Missing port number for enqueue action");
+                return null;
+            }
 
-	/**
-	 * Parse set_nw_src actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder.
-	 * 
-	 * TODO should consider using IPv4AddressWithMask's built-in parser....
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFActionSetNwSrc decode_set_src_ip(String actionToDecode, OFVersion version, Logger log) {
-		Matcher n = Pattern.compile("(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(actionToDecode);
-		if (n.matches()) {
-			IPv4Address ipaddr = IPv4Address.of(get_ip_addr(n, actionToDecode, log));
-			OFActionSetNwSrc.Builder ab = OFFactories.getFactory(version).actions().buildSetNwSrc();
-			ab.setNwAddr(ipaddr);
-			log.debug("action {}", ab.build());
-			return ab.build();
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-	}
+            int queueid = 0;
+            if (n.group(2) != null) {
+                try {
+                    queueid = ParseUtils.parseHexOrDecInt(n.group(2));
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid queue-id in: '{}' (error ignored)", actionToDecode);
+                    return null;
+                }
+            }
+            OFActionEnqueue a = OFFactories.getFactory(version).actions().buildEnqueue()
+                    .setPort(port)
+                    .setQueueId(queueid)
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        }
+        else {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * Parse set_nw_dst actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder.
-	 * 
-	 * TODO should consider using IPv4AddressWithMask's built-in parser....
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFActionSetNwDst decode_set_dst_ip(String actionToDecode, OFVersion version, Logger log) {
-		Matcher n = Pattern.compile("(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(actionToDecode);
-		if (n.matches()) {
-			IPv4Address ipaddr = IPv4Address.of(get_ip_addr(n, actionToDecode, log));
-			OFActionSetNwDst.Builder ab = OFFactories.getFactory(version).actions().buildSetNwDst();
-			ab.setNwAddr(ipaddr);
-			log.debug("action {}", ab.build());
-			return ab.build();
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-	}
+    /**
+     * Parse set_vlan_id actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. Data with a leading 0x is permitted.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetVlanVid decode_set_vlan_id(String actionToDecode, OFVersion version) {
+        Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode);
+        if (n.matches()) {            
+            if (n.group(1) != null) {
+                try {
+                    VlanVid vlanid = VlanVid.ofVlan(ParseUtils.parseHexOrDecShort(n.group(1)));
+                    OFActionSetVlanVid a = OFFactories.getFactory(version).actions().buildSetVlanVid()
+                            .setVlanVid(vlanid)
+                            .build();
+                    log.debug("action {}", a);
+                    return a;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid VLAN in: {} (error ignored)", actionToDecode);
+                    return null;
+                }
+            }          
+        }
+        else {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+        return null;
+    }
 
-	/**
-	 * Parse set_tp_src actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. A leading 0x is permitted.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFActionSetTpSrc decode_set_src_port(String actionToDecode, OFVersion version, Logger log) {
-		Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); 
-		if (n.matches()) {
-			if (n.group(1) != null) {
-				try {
-					TransportPort portnum = TransportPort.of(get_int(n.group(1)));
-					OFActionSetTpSrc.Builder ab = OFFactories.getFactory(version).actions().buildSetTpSrc();
-					ab.setTpPort(portnum);
-					log.debug("action {}", ab.build());
-					return ab.build();
-				} 
-				catch (NumberFormatException e) {
-					log.debug("Invalid src-port in: {} (error ignored)", actionToDecode);
-					return null;
-				}
-			}
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-		return null;
-	}
+    /**
+     * Parse set_vlan_pcp actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. Data with a leading 0x is permitted.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetVlanPcp decode_set_vlan_priority(String actionToDecode, OFVersion version) {
+        Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); 
+        if (n.matches()) {            
+            if (n.group(1) != null) {
+                try {
+                    OFActionSetVlanPcp a = OFFactories.getFactory(version).actions().buildSetVlanPcp()
+                            .setVlanPcp(VlanPcp.of(ParseUtils.parseHexOrDecByte(n.group(1))))
+                            .build();
+                    log.debug("action {}", a);
+                    return a;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid VLAN priority in: {} (error ignored)", actionToDecode);
+                    return null;
+                }
+            }
+        }
+        else {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+        return null;
+    }
 
-	/**
-	 * Parse set_tp_dst actions.
-	 * The key and delimiter for the action should be omitted, and only the
-	 * data should be presented to this decoder. A leading 0x is permitted.
-	 * 
-	 * @param actionToDecode; The action as a string to decode
-	 * @param version; The OF version to create the action for
-	 * @param log
-	 * @return
-	 */
-	private static OFAction decode_set_dst_port(String actionToDecode, OFVersion version, Logger log) {
-		Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode);
-		if (n.matches()) {
-			if (n.group(1) != null) {
-				try {
-					TransportPort portnum = TransportPort.of(get_int(n.group(1)));
-					OFActionSetTpDst.Builder ab = OFFactories.getFactory(version).actions().buildSetTpDst();
-					ab.setTpPort(portnum);
-					log.debug("action {}", ab.build());
-					return ab.build();
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid dst-port in: {} (error ignored)", actionToDecode);
-					return null;
-				}
-			}
-		}
-		else {
-			log.debug("Invalid action: '{}'", actionToDecode);
-			return null;
-		}
-		return null;
-	}
+    /**
+     * Parse set_dl_src actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetDlSrc decode_set_src_mac(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetDlSrc a = OFFactories.getFactory(version).actions().buildSetDlSrc()
+                    .setDlAddr(MacAddress.of(actionToDecode))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        }
+        catch (Exception e) {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * This is out of date and should be replaced with the built-in parser of MacAddress
-	 * @param n
-	 * @param actionToDecode
-	 * @param log
-	 * @return
-	 */
-	private static byte[] get_mac_addr(Matcher n, String actionToDecode, Logger log) {
-		byte[] macaddr = new byte[6];     
-		for (int i=0; i<6; i++) {
-			if (n.group(i+1) != null) {
-				try {
-					macaddr[i] = get_byte("0x" + n.group(i+1));
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid src-mac in: '{}' (error ignored)", actionToDecode);
-					return null;
-				}
-			}
-			else { 
-				log.debug("Invalid src-mac in: '{}' (null, error ignored)", actionToDecode);
-				return null;
-			}
-		}  
-		return macaddr;
-	}
+    /**
+     * Parse set_dl_dst actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetDlDst decode_set_dst_mac(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetDlDst a = OFFactories.getFactory(version).actions().buildSetDlDst()
+                    .setDlAddr(MacAddress.of(actionToDecode))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        }
+        catch (Exception e) {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * This is out of date and should be replaced with the built-in parser of IPv4AddressWithMask
-	 * @param n
-	 * @param actionToDecode
-	 * @param log
-	 * @return
-	 */
-	private static int get_ip_addr(Matcher n, String actionToDecode, Logger log) {
-		int ipaddr = 0;
-		for (int i=0; i<4; i++) {
-			if (n.group(i+1) != null) {
-				try {
-					ipaddr = ipaddr<<8;
-					ipaddr = ipaddr | get_int(n.group(i+1));
-				}
-				catch (NumberFormatException e) {
-					log.debug("Invalid src-ip in: '{}' (error ignored)", actionToDecode);
-					return 0;
-				}
-			}
-			else {
-				log.debug("Invalid src-ip in: '{}' (null, error ignored)", actionToDecode);
-				return 0;
-			}
-		}
-		return ipaddr;
-	}
+    /**
+     * Parse set_tos actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. A leading 0x is permitted.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetNwTos decode_set_tos_bits(String actionToDecode, OFVersion version) {
+        Matcher n = Pattern.compile("((?:0x)?\\d+)").matcher(actionToDecode); 
+        if (n.matches()) {
+            if (n.group(1) != null) {
+                try {
+                    OFActionSetNwTos a = OFFactories.getFactory(version).actions().buildSetNwTos()
+                            .setNwTos(ParseUtils.parseHexOrDecByte(n.group(1)))
+                            .build();
+                    log.debug("action {}", a);
+                    return a;
+                }
+                catch (NumberFormatException e) {
+                    log.debug("Invalid dst-port in: {} (error ignored)", actionToDecode);
+                    return null;
+                }
+            }
+        }
+        else {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+        return null;
+    }
 
-	/**
-	 * Parse int as decimal, hex (start with 0x or #) or octal (starts with 0)
-	 * @param str
-	 * @return
-	 */
-	private static int get_int(String str) {
-		return Integer.decode(str);
-	}
+    /**
+     * Parse set_nw_src actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetNwSrc decode_set_src_ip(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetNwSrc a = OFFactories.getFactory(version).actions().buildSetNwSrc()
+                    .setNwAddr(IPv4Address.of(actionToDecode))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        } catch (Exception e) {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * Parse short as decimal, hex (start with 0x or #) or octal (starts with 0)
-	 * @param str
-	 * @return
-	 */
-	private static short get_short(String str) {
-		return (short)(int)Integer.decode(str);
-	}
+    /**
+     * Parse set_nw_dst actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetNwDst decode_set_dst_ip(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetNwDst a = OFFactories.getFactory(version).actions().buildSetNwDst()
+                    .setNwAddr(IPv4Address.of(actionToDecode))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        } catch (Exception e) {
+            log.debug("Invalid action: '{}'", actionToDecode);
+            return null;
+        }
+    }
 
-	/**
-	 * Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0)
-	 * @param str
-	 * @return
-	 */
-	private static byte get_byte(String str) {
-		return Integer.decode(str).byteValue();
-	}
+    /**
+     * Parse set_tp_src actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. A leading 0x is permitted.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFActionSetTpSrc decode_set_src_port(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetTpSrc a = OFFactories.getFactory(version).actions().buildSetTpSrc()
+                    .setTpPort(TransportPort.of(Integer.parseInt(actionToDecode)))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        } 
+        catch (NumberFormatException e) {
+            log.debug("Invalid src-port in: {} (error ignored)", actionToDecode);
+            return null;
+        }
+    }
 
-}
+    /**
+     * Parse set_tp_dst actions.
+     * The key and delimiter for the action should be omitted, and only the
+     * data should be presented to this decoder. A leading 0x is permitted.
+     * 
+     * @param actionToDecode; The action as a string to decode
+     * @param version; The OF version to create the action for
+     * @return
+     */
+    private static OFAction decode_set_dst_port(String actionToDecode, OFVersion version) {
+        try {
+            OFActionSetTpSrc a = OFFactories.getFactory(version).actions().buildSetTpSrc()
+                    .setTpPort(TransportPort.of(Integer.parseInt(actionToDecode)))
+                    .build();
+            log.debug("action {}", a);
+            return a;
+        } 
+        catch (NumberFormatException e) {
+            log.debug("Invalid src-port in: {} (error ignored)", actionToDecode);
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/FlowModUtils.java b/src/main/java/net/floodlightcontroller/util/FlowModUtils.java
index 8f7645b725bdcc467b6c5e98fce6b3247d7cffe3..cf536e8e33ab3231e96db89bde57c020efb7dca6 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowModUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowModUtils.java
@@ -42,9 +42,11 @@ public class FlowModUtils {
 		OFVersion version = fm.getVersion();
 		OFFlowAdd.Builder b = OFFactories.getFactory(version).buildFlowAdd();
 
+		if (!(fm instanceof OFFlowDelete) && !(fm instanceof OFFlowDeleteStrict)) {
+			b.setActions(fm.getActions());
+		}
 		if (b.getVersion().compareTo(OFVersion.OF_10) == 0) {
-			return b.setActions(fm.getActions())
-					.setBufferId(fm.getBufferId())
+			return b.setBufferId(fm.getBufferId())
 					.setCookie(fm.getCookie())
 					// cookie-mask not supported
 					.setFlags(fm.getFlags())
@@ -59,8 +61,7 @@ public class FlowModUtils {
 					.setXid(fm.getXid())
 					.build();
 		} else {
-			return b.setActions(fm.getActions())
-					.setBufferId(fm.getBufferId())
+			return b.setBufferId(fm.getBufferId())
 					.setCookie(fm.getCookie())
 					.setCookieMask(fm.getCookieMask()) // added in OF1.1
 					.setFlags(fm.getFlags())
@@ -80,9 +81,9 @@ public class FlowModUtils {
 	public static OFFlowDelete toFlowDelete(OFFlowMod fm) {
 		OFVersion version = fm.getVersion();
 		OFFlowDelete.Builder b = OFFactories.getFactory(version).buildFlowDelete();
+		/* ignore actions */
 		if (b.getVersion().compareTo(OFVersion.OF_10) == 0) {
-			return b.setActions(fm.getActions())
-					.setBufferId(fm.getBufferId())
+			return b.setBufferId(fm.getBufferId())
 					.setCookie(fm.getCookie())
 					// cookie-mask not supported
 					.setFlags(fm.getFlags())
@@ -97,8 +98,7 @@ public class FlowModUtils {
 					.setXid(fm.getXid())
 					.build();
 		} else {
-			return b.setActions(fm.getActions())
-					.setBufferId(fm.getBufferId())
+			return b.setBufferId(fm.getBufferId())
 					.setCookie(fm.getCookie())
 					.setCookieMask(fm.getCookieMask()) // added in OF1.1
 					.setFlags(fm.getFlags())
@@ -118,9 +118,9 @@ public class FlowModUtils {
 	public static OFFlowDeleteStrict toFlowDeleteStrict(OFFlowMod fm) {
 		OFVersion version = fm.getVersion();
 		OFFlowDeleteStrict.Builder b = OFFactories.getFactory(version).buildFlowDeleteStrict();
+		/* ignore actions */
 		if (b.getVersion().compareTo(OFVersion.OF_10) == 0) {
-			return b.setActions(fm.getActions())
-					.setBufferId(fm.getBufferId())
+			return b.setBufferId(fm.getBufferId())
 					.setCookie(fm.getCookie())
 					// cookie-mask not supported
 					.setFlags(fm.getFlags())
@@ -135,8 +135,7 @@ public class FlowModUtils {
 					.setXid(fm.getXid())
 					.build();
 		} else {
-			return b.setActions(fm.getActions())
-					.setBufferId(fm.getBufferId())
+			return b.setBufferId(fm.getBufferId())
 					.setCookie(fm.getCookie())
 					.setCookieMask(fm.getCookieMask()) // added in OF1.1
 					.setFlags(fm.getFlags())
@@ -156,9 +155,11 @@ public class FlowModUtils {
 	public static OFFlowModify toFlowModify(OFFlowMod fm) {
 		OFVersion version = fm.getVersion();
 		OFFlowModify.Builder b = OFFactories.getFactory(version).buildFlowModify();
+		if (!(fm instanceof OFFlowDelete) && !(fm instanceof OFFlowDeleteStrict)) {
+			b.setActions(fm.getActions());
+		}
 		if (b.getVersion().compareTo(OFVersion.OF_10) == 0) {
-			return b.setActions(fm.getActions())
-					.setBufferId(fm.getBufferId())
+			return b.setBufferId(fm.getBufferId())
 					.setCookie(fm.getCookie())
 					// cookie-mask not supported
 					.setFlags(fm.getFlags())
@@ -173,8 +174,7 @@ public class FlowModUtils {
 					.setXid(fm.getXid())
 					.build();
 		} else {
-			return b.setActions(fm.getActions())
-					.setBufferId(fm.getBufferId())
+			return b.setBufferId(fm.getBufferId())
 					.setCookie(fm.getCookie())
 					.setCookieMask(fm.getCookieMask()) // added in OF1.1
 					.setFlags(fm.getFlags())
@@ -194,9 +194,11 @@ public class FlowModUtils {
 	public static OFFlowModifyStrict toFlowModifyStrict(OFFlowMod fm) {
 		OFVersion version = fm.getVersion();
 		OFFlowModifyStrict.Builder b = OFFactories.getFactory(version).buildFlowModifyStrict();
+		if (!(fm instanceof OFFlowDelete) && !(fm instanceof OFFlowDeleteStrict)) {
+			b.setActions(fm.getActions());
+		}
 		if (b.getVersion().compareTo(OFVersion.OF_10) == 0) {
-			return b.setActions(fm.getActions())
-					.setBufferId(fm.getBufferId())
+			return b.setBufferId(fm.getBufferId())
 					.setCookie(fm.getCookie())
 					// cookie-mask not supported
 					.setFlags(fm.getFlags())
@@ -211,8 +213,7 @@ public class FlowModUtils {
 					.setXid(fm.getXid())
 					.build();
 		} else {
-			return b.setActions(fm.getActions())
-					.setBufferId(fm.getBufferId())
+			return b.setBufferId(fm.getBufferId())
 					.setCookie(fm.getCookie())
 					.setCookieMask(fm.getCookieMask()) // added in OF1.1
 					.setFlags(fm.getFlags())
diff --git a/src/main/java/net/floodlightcontroller/util/GroupUtils.java b/src/main/java/net/floodlightcontroller/util/GroupUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..55cb7848af93aad7918254b33707ca2ab6db58dd
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/GroupUtils.java
@@ -0,0 +1,377 @@
+package net.floodlightcontroller.util;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.projectfloodlight.openflow.protocol.OFBucket;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFGroupAdd;
+import org.projectfloodlight.openflow.protocol.OFGroupDelete;
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+import org.projectfloodlight.openflow.protocol.OFGroupModify;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
+import org.projectfloodlight.openflow.types.OFGroup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.JsonGenerator.Feature;
+import com.fasterxml.jackson.databind.MappingJsonFactory;
+
+/**
+ * Convert OFGroups to and from JSON strings. Used primarily by 
+ * the static flow pusher to store and retrieve flow entries.
+ * 
+ * @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu>
+ *
+ */
+public class GroupUtils {
+    private static final Logger log = LoggerFactory.getLogger(GroupUtils.class);
+
+    public static final String GROUP_ID = "group_id";
+    public static final String GROUP_ID_MAX = "max";
+    public static final String GROUP_ID_ANY = "any";
+    public static final String GROUP_ID_ALL = "all";
+
+    public static final String GROUP_TYPE = "group_type";
+    public static final String GROUP_TYPE_FF = "fast_failover";
+    public static final String GROUP_TYPE_ALL = "all";
+    public static final String GROUP_TYPE_SELECT = "select";
+    public static final String GROUP_TYPE_INDIRECT = "indirect";
+
+    public static final String GROUP_BUCKETS = "group_buckets";
+    public static final String BUCKET_ID = "bucket_id";
+    public static final String BUCKET_WEIGHT = "bucket_weight";
+    public static final String BUCKET_WATCH_PORT = "bucket_watch_port";
+    public static final String BUCKET_WATCH_GROUP = "bucket_watch_group";
+    public static final String BUCKET_ACTIONS = "bucket_actions";
+
+    private static final JsonFactory jsonFactory = new JsonFactory();
+
+    private static final String JSON_EMPTY_ARRAY = "[]";
+    private static final String JSON_EMPTY_VALUE = "";
+
+    private GroupUtils() { }
+
+    public static OFGroupModify toGroupModify(OFGroupMod gm) {
+        return OFFactories.getFactory(gm.getVersion()).buildGroupModify()
+                .setBuckets(gm.getBuckets())
+                .setGroup(gm.getGroup())
+                .setGroupType(gm.getGroupType())
+                .setXid(gm.getXid())
+                .build();
+    }
+
+    public static OFGroupDelete toGroupDelete(OFGroupMod gm) {
+        return OFFactories.getFactory(gm.getVersion()).buildGroupDelete()
+                /* don't care about buckets or type */
+                .setGroup(gm.getGroup())
+                .setGroupType(gm.getGroupType())
+                .setXid(gm.getXid())
+                .build();
+    }
+
+    public static OFGroupAdd toGroupAdd(OFGroupMod gm) {
+        return OFFactories.getFactory(gm.getVersion()).buildGroupAdd()
+                .setBuckets(gm.getBuckets())
+                .setGroup(gm.getGroup())
+                .setGroupType(gm.getGroupType())
+                .setXid(gm.getXid())
+                .build();
+    }
+
+    public static boolean setGroupIdFromString(OFGroupMod.Builder g, String s) {
+        if (g == null) {
+            throw new IllegalArgumentException("OFGroupMod cannot be null");
+        }
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+        OFGroup group = groupIdFromString(s);
+        if (group == null) {
+            log.error("Could not set group ID {} due to parse error", s);
+            return false;
+        } else {
+            g.setGroup(group);
+            return true;
+        }	
+    }
+
+    public static OFGroup groupIdFromString(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+        s = s.trim().toLowerCase();
+        try {
+            if (s.equals(GROUP_ID_ALL)) {
+                return OFGroup.ALL;
+            } else if (s.equals(GROUP_ID_ANY)) {
+                return OFGroup.ANY;
+            } else if (s.equals(GROUP_ID_MAX)) {
+                return OFGroup.MAX;
+            } else {
+                return OFGroup.of(ParseUtils.parseHexOrDecInt(s));
+            }
+        } catch (Exception e) {
+            log.error("Could not parse group ID {}", s);
+            return null;
+        }
+    }
+
+    public static String groupIdToString(OFGroup g) {
+        if (g == null) {
+            throw new IllegalArgumentException("Group ID cannot be null");
+        }
+        if (g.equals(OFGroup.ALL)) {
+            return GROUP_ID_ALL;
+        }
+        if (g.equals(OFGroup.ANY)) {
+            return GROUP_ID_ANY;
+        }
+        if (g.equals(OFGroup.MAX)) {
+            return GROUP_ID_MAX;
+        }
+        return Integer.toString(g.getGroupNumber());
+    }
+
+    public static OFGroupType groupTypeFromString(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+
+        s = s.trim().toLowerCase();
+
+        switch (s) {
+        case GROUP_TYPE_ALL:
+            return OFGroupType.ALL;
+        case GROUP_TYPE_FF:
+            return OFGroupType.FF;
+        case GROUP_TYPE_INDIRECT:
+            return OFGroupType.INDIRECT;
+        case GROUP_TYPE_SELECT:
+            return OFGroupType.SELECT;
+        default:
+            log.error("Unrecognized group type {}", s);
+            return null;
+        }
+    }
+
+    public static boolean setGroupTypeFromString(OFGroupMod.Builder g, String s) {
+        if (g == null) {
+            throw new IllegalArgumentException("OFGroupMod cannot be null");
+        }
+
+        OFGroupType t = groupTypeFromString(s);
+        if (t != null) {
+            g.setGroupType(t);
+            return true;
+        }
+        return false;
+    }
+
+    public static String groupTypeToString(OFGroupType t) {
+        if (t == null) {
+            throw new IllegalArgumentException("OFGroupType cannot be null");
+        }
+
+        switch (t) {
+        case ALL:
+            return GROUP_TYPE_ALL;
+        case FF:
+            return GROUP_TYPE_FF;
+        case INDIRECT:
+            return GROUP_TYPE_INDIRECT;
+        case SELECT:
+            return GROUP_TYPE_SELECT;
+        default:
+            log.error("Unrecognized group type {}", t);
+            return JSON_EMPTY_VALUE;
+        }
+    }
+
+    /**
+     * Append an array of buckets to an existing JsonGenerator.
+     * This method assumes the field name of the array has been
+     * written already, if required. The appended data will
+     * be formatted as follows:
+     *   [
+     *     {
+     *       bucket-1
+     *     },
+     *     {
+     *       bucket-2
+     *     },
+     *     ...,
+     *     {
+     *       bucket-n
+     *     }
+     *   ]
+     * @param jsonGen
+     * @param bucketList
+     */
+    public static void groupBucketsToJsonArray(JsonGenerator jsonGen, List<OFBucket> bucketList) {
+        jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);
+
+        try {
+            int bucketId = 1;
+            jsonGen.writeStartArray();
+            for (OFBucket b : bucketList) {
+                jsonGen.writeStartObject();
+                jsonGen.writeNumberField(BUCKET_ID, bucketId++); /* not preserved from original, but indicates order */
+                jsonGen.writeStringField(BUCKET_WATCH_GROUP, groupIdToString(b.getWatchGroup()));
+                jsonGen.writeStringField(BUCKET_WATCH_PORT, MatchUtils.portToString(b.getWatchPort()));
+                jsonGen.writeNumberField(BUCKET_WEIGHT, b.getWeight());
+                jsonGen.writeStringField(BUCKET_ACTIONS, ActionUtils.actionsToString(b.getActions())); /* TODO update to object array */
+                jsonGen.writeEndObject();
+            }
+            jsonGen.writeEndArray();
+        } catch (IOException e) {
+            log.error("Error composing group bucket JSON array. {}", e.getMessage());
+            return;
+        }
+    }
+
+    /**
+     * Convert a list of group buckets into a JSON-formatted
+     * string. The string data will be formatted as follows:
+     *   [
+     *     {
+     *       bucket-1
+     *     },
+     *     {
+     *       bucket-2
+     *     },
+     *     ...,
+     *     {
+     *       bucket-n
+     *     }
+     *   ]
+     * @param bucketList
+     * @return the string, formatted as described above
+     */
+    public static String groupBucketsToJsonArray(List<OFBucket> bucketList) {
+        Writer w = new StringWriter();
+        JsonGenerator jsonGen;
+        try {
+            jsonGen = jsonFactory.createGenerator(w);
+        } catch (IOException e) {
+            log.error("Could not instantiate JSON Generator. {}", e.getMessage());
+            return JSON_EMPTY_ARRAY;
+        }
+
+        groupBucketsToJsonArray(jsonGen, bucketList);
+
+        return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */
+    }
+
+    /**
+     * Convert a JSON-formatted string of group buckets to a
+     * Java list of buckets. The JSON format expected is:
+     *   [
+     *     {
+     *       bucket-1
+     *     },
+     *     {
+     *       bucket-2
+     *     },
+     *     ...,
+     *     {
+     *       bucket-n
+     *     }
+     *   ]
+     * @param g the group-mod message to add the buckets
+     * @param json the string, formatted as described above
+     */
+    public static boolean setGroupBucketsFromJsonArray(OFGroupMod.Builder g, String json) {
+        final Map<Integer, OFBucket> bucketsById = new HashMap<Integer, OFBucket>();
+        final MappingJsonFactory f = new MappingJsonFactory();
+
+        if (g == null) {
+            throw new IllegalArgumentException("OFGroupMod cannot be null");
+        }
+        if (json == null) {
+            throw new IllegalArgumentException("JSON string cannot be null");
+        }
+
+        final JsonParser jp;
+        try {
+            jp = f.createParser(json);
+        } catch (IOException e) {
+            log.error("Could not create JSON parser for {}", json);
+            return false;
+        }
+
+        try {
+            if (jp.nextToken() != JsonToken.START_ARRAY) {
+                throw new IOException("Expected START_ARRAY");
+            }
+
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+                    throw new IOException("Expected START_OBJECT");
+                }
+
+                OFBucket.Builder b = OFFactories.getFactory(g.getVersion()).buildBucket();
+                int bucketId = -1;
+
+                while (jp.nextToken() != JsonToken.END_OBJECT) {
+                    if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
+                        throw new IOException("Expected FIELD_NAME");
+                    }
+
+                    String key = jp.getCurrentName().toLowerCase().trim();
+                    jp.nextToken();
+                    String value = jp.getText().toLowerCase().trim();
+                    switch (key) {
+                    case BUCKET_ID:
+                        bucketId = ParseUtils.parseHexOrDecInt(value);
+                        break;
+                    case BUCKET_WATCH_GROUP:
+                        b.setWatchGroup(groupIdFromString(value));
+                        break;
+                    case BUCKET_WATCH_PORT:
+                        b.setWatchPort(MatchUtils.portFromString(value));
+                        break;
+                    case BUCKET_WEIGHT:
+                        b.setWeight(ParseUtils.parseHexOrDecInt(value));
+                        break;
+                    case BUCKET_ACTIONS:
+                        b.setActions(ActionUtils.fromString(value, b.getVersion())); // TODO update to JSON 
+                        break;
+                    default:
+                        log.warn("Unknown bucket key {}", key);
+                        break;
+                    }
+                }
+                if (bucketId != -1) {
+                    bucketsById.put(bucketId, b.build());
+                } else {
+                    log.error("Must provide a bucket ID for bucket {}", b);
+                }
+            }
+        } catch (IOException e) {
+            log.error("Could not parse: {}", json);
+            log.error("JSON parse error message: {}", e.getMessage());
+            return false;
+        }
+
+        g.setBuckets(
+                bucketsById.entrySet()
+                .stream()
+                .sorted( /* invert to get sorted smallest to largest */
+                        (e1, e2) -> Integer.compare(e1.getKey(), e2.getKey())
+                        )
+                .map(Map.Entry::getValue)
+                .collect(Collectors.toList())
+                );
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/InstructionUtils.java b/src/main/java/net/floodlightcontroller/util/InstructionUtils.java
index aa89c1b581156c6dbdda3f75107bf2db74dfbe9b..281325867ede8929247775559d78d853fddc8189 100644
--- a/src/main/java/net/floodlightcontroller/util/InstructionUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/InstructionUtils.java
@@ -1,10 +1,18 @@
 package net.floodlightcontroller.util;
 
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFInstructionType;
+import org.projectfloodlight.openflow.protocol.OFOxsList;
+import org.projectfloodlight.openflow.protocol.OFStatTriggerFlags;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
@@ -12,11 +20,27 @@ import org.projectfloodlight.openflow.protocol.instruction.OFInstructionClearAct
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionExperimenter;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionMeter;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionStatTrigger;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteActions;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteMetadata;
+import org.projectfloodlight.openflow.protocol.oxs.OFOxs;
+import org.projectfloodlight.openflow.protocol.oxs.OFOxsByteCount;
+import org.projectfloodlight.openflow.protocol.oxs.OFOxsDuration;
+import org.projectfloodlight.openflow.protocol.oxs.OFOxsFlowCount;
+import org.projectfloodlight.openflow.protocol.oxs.OFOxsIdleTime;
+import org.projectfloodlight.openflow.protocol.oxs.OFOxsPacketCount;
+import org.projectfloodlight.openflow.protocol.stat.StatField;
 import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U32;
 import org.projectfloodlight.openflow.types.U64;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.JsonGenerator.Feature;
 
 /**
  * Convert OFInstructions to and from dpctl/ofctl-style strings.
@@ -27,6 +51,8 @@ import org.slf4j.Logger;
  *
  */
 public class InstructionUtils {
+	private static final Logger log = LoggerFactory.getLogger(InstructionUtils.class);
+
 	public static final String STR_GOTO_TABLE = "instruction_goto_table";
 	public static final String STR_WRITE_METADATA = "instruction_write_metadata";
 	public static final String STR_WRITE_ACTIONS = "instruction_write_actions";
@@ -34,6 +60,11 @@ public class InstructionUtils {
 	public static final String STR_CLEAR_ACTIONS = "instruction_clear_actions";
 	public static final String STR_GOTO_METER = "instruction_goto_meter";
 	public static final String STR_EXPERIMENTER = "instruction_experimenter";
+	public static final String STR_DEPRECATED = "instruction_deprecated";
+	public static final String STR_STAT_TRIGGER = "instruction_stat_trigger";
+	
+	private static final JsonFactory jsonFactory = new JsonFactory();
+	private static final String JSON_EMPTY_OBJECT = "{}";
 
 	/** 
 	 * Adds the instructions to the list of OFInstructions in the OFFlowMod. Any pre-existing
@@ -57,16 +88,44 @@ public class InstructionUtils {
 		fmb.setInstructions(newIl);
 	}
 
+	/**
+	 * Get string name of OFInstructionType
+	 * @param t
+	 * @return
+	 */
+	public static String getInstructionName(OFInstructionType t) {
+		switch (t) {
+		case APPLY_ACTIONS:
+			return STR_APPLY_ACTIONS;
+		case CLEAR_ACTIONS:
+			return STR_CLEAR_ACTIONS;
+		case DEPRECATED:
+			return STR_DEPRECATED;
+		case EXPERIMENTER:
+			return STR_EXPERIMENTER;
+		case GOTO_TABLE:
+			return STR_GOTO_TABLE;
+		case METER:
+			return STR_GOTO_METER;
+		case STAT_TRIGGER:
+			return STR_STAT_TRIGGER;
+		case WRITE_ACTIONS:
+			return STR_WRITE_ACTIONS;
+		case WRITE_METADATA:
+			return STR_WRITE_METADATA;	
+		}
+		return "";
+	}
+
 	///////////////////////////////////////////////////////////////////////////////////////////////////
 
 	/**
 	 * Convert an OFInstructionGotoTable to string form. The string will be formatted
 	 * in a dpctl/ofctl-style syntax.
 	 * @param inst; The instruction to convert to a string
-	 * @param log
 	 * @return
 	 */
-	public static String gotoTableToString(OFInstructionGotoTable inst, Logger log) {
+	public static String gotoTableToString(OFInstructionGotoTable inst) {
 		return Short.toString(inst.getTableId().getValue());
 	}
 
@@ -77,9 +136,8 @@ public class InstructionUtils {
 	 * side effect is the addition of an instruction in the OFFlowMod.Builder.
 	 * @param fmb; The FMB in which to append the new instruction
 	 * @param instStr; The string to parse the instruction from
-	 * @param log
 	 */
-	public static void gotoTableFromString(OFFlowMod.Builder fmb, String inst, Logger log) {
+	public static void gotoTableFromString(OFFlowMod.Builder fmb, String inst) {
 		if (inst == null || inst.equals("")) {
 			return;
 		}
@@ -92,11 +150,7 @@ public class InstructionUtils {
 		OFInstructionGotoTable.Builder ib = OFFactories.getFactory(fmb.getVersion()).instructions().buildGotoTable();
 
 		// Get the table ID
-		if (inst.startsWith("0x")) {
-			ib.setTableId(TableId.of(Integer.parseInt(inst.replaceFirst("0x", ""), 16)));
-		} else {
-			ib.setTableId(TableId.of(Integer.parseInt(inst))).build();
-		}
+		ib.setTableId(TableId.of(ParseUtils.parseHexOrDecInt(inst)));
 
 		log.debug("Appending GotoTable instruction: {}", ib.build());
 		appendInstruction(fmb, ib.build());
@@ -107,10 +161,9 @@ public class InstructionUtils {
 	 * Convert an OFInstructionMetadata to string form. The string will be formatted
 	 * in a dpctl/ofctl-style syntax.
 	 * @param inst; The instruction to convert to a string
-	 * @param log
 	 * @return
 	 */
-	public static String writeMetadataToString(OFInstructionWriteMetadata inst, Logger log) {
+	public static String writeMetadataToString(OFInstructionWriteMetadata inst) {
 		/* 
 		 * U64.toString() formats with a leading 0x
 		 */
@@ -128,9 +181,8 @@ public class InstructionUtils {
 	 * side effect is the addition of an instruction in the OFFlowMod.Builder.
 	 * @param fmb; The FMB in which to append the new instruction
 	 * @param instStr; The string to parse the instruction from
-	 * @param log
 	 */
-	public static void writeMetadataFromString(OFFlowMod.Builder fmb, String inst, Logger log) {
+	public static void writeMetadataFromString(OFFlowMod.Builder fmb, String inst) {
 		if (inst == null || inst.equals("")) {
 			return;
 		}
@@ -153,19 +205,11 @@ public class InstructionUtils {
 		}
 
 		// Get the metadata
-		if (keyValue[0].startsWith("0x")) {
-			ib.setMetadata(U64.of(Long.valueOf(keyValue[0].replaceFirst("0x", ""), 16)));
-		} else {
-			ib.setMetadata(U64.of(Long.valueOf(keyValue[0])));
-		}
+		ib.setMetadata(U64.of(ParseUtils.parseHexOrDecLong(keyValue[0])));
 
 		// Get the optional mask
 		if (keyValue.length == 2) {
-			if (keyValue[1].startsWith("0x")) {
-				ib.setMetadataMask(U64.of(Long.valueOf(keyValue[1].replaceFirst("0x", ""), 16)));
-			} else {
-				ib.setMetadataMask(U64.of(Long.valueOf(keyValue[1])));
-			}
+			ib.setMetadataMask(U64.of(ParseUtils.parseHexOrDecLong(keyValue[1])));
 		} else {
 			ib.setMetadataMask(U64.NO_MASK);
 		}
@@ -181,11 +225,10 @@ public class InstructionUtils {
 	 * Convert an OFInstructionWriteActions to string form. The string will be formatted
 	 * in a dpctl/ofctl-style syntax.
 	 * @param inst; The instruction to convert to a string
-	 * @param log
 	 * @return
 	 */
-	public static String writeActionsToString(OFInstructionWriteActions inst, Logger log) throws Exception {
-		return ActionUtils.actionsToString(inst.getActions(), log);
+	public static String writeActionsToString(OFInstructionWriteActions inst) throws Exception {
+		return ActionUtils.actionsToString(inst.getActions());
 	}
 
 	/**
@@ -195,9 +238,8 @@ public class InstructionUtils {
 	 * side effect is the addition of an instruction in the OFFlowMod.Builder.
 	 * @param fmb; The FMB in which to append the new instruction
 	 * @param instStr; The string to parse the instruction from
-	 * @param log
 	 */
-	public static void writeActionsFromString(OFFlowMod.Builder fmb, String inst, Logger log) {
+	public static void writeActionsFromString(OFFlowMod.Builder fmb, String inst) {
 
 		if (fmb.getVersion().compareTo(OFVersion.OF_11) < 0) {
 			log.error("Write Actions Instruction not supported in OpenFlow 1.0");
@@ -206,7 +248,7 @@ public class InstructionUtils {
 
 		OFFlowMod.Builder tmpFmb = OFFactories.getFactory(fmb.getVersion()).buildFlowModify(); // ActionUtils.fromString() will use setActions(), which should not be used for OF1.3; use temp to avoid overwriting any applyActions data
 		OFInstructionWriteActions.Builder ib = OFFactories.getFactory(fmb.getVersion()).instructions().buildWriteActions();
-		ActionUtils.fromString(tmpFmb, inst, log);
+		ActionUtils.fromString(tmpFmb, inst);
 		ib.setActions(tmpFmb.getActions());
 		log.debug("Appending WriteActions instruction: {}", ib.build());
 		appendInstruction(fmb, ib.build());
@@ -218,11 +260,10 @@ public class InstructionUtils {
 	 * Convert an OFInstructionApplyActions to string form. The string will be formatted
 	 * in a dpctl/ofctl-style syntax.
 	 * @param inst; The instruction to convert to a string
-	 * @param log
 	 * @return
 	 */
-	public static String applyActionsToString(OFInstructionApplyActions inst, Logger log) throws Exception {
-		return ActionUtils.actionsToString(inst.getActions(), log);
+	public static String applyActionsToString(OFInstructionApplyActions inst) throws Exception {
+		return ActionUtils.actionsToString(inst.getActions());
 	}
 
 	/**
@@ -232,9 +273,8 @@ public class InstructionUtils {
 	 * side effect is the addition of an instruction in the OFFlowMod.Builder.
 	 * @param fmb; The FMB in which to append the new instruction
 	 * @param instStr; The string to parse the instruction from
-	 * @param log
 	 */
-	public static void applyActionsFromString(OFFlowMod.Builder fmb, String inst, Logger log) {
+	public static void applyActionsFromString(OFFlowMod.Builder fmb, String inst) {
 
 		if (fmb.getVersion().compareTo(OFVersion.OF_11) < 0) {
 			log.error("Apply Actions Instruction not supported in OpenFlow 1.0");
@@ -243,7 +283,7 @@ public class InstructionUtils {
 
 		OFFlowMod.Builder tmpFmb = OFFactories.getFactory(fmb.getVersion()).buildFlowModify();
 		OFInstructionApplyActions.Builder ib = OFFactories.getFactory(fmb.getVersion()).instructions().buildApplyActions();
-		ActionUtils.fromString(tmpFmb, inst, log);
+		ActionUtils.fromString(tmpFmb, inst);
 		ib.setActions(tmpFmb.getActions());
 		log.debug("Appending ApplyActions instruction: {}", ib.build());
 		appendInstruction(fmb, ib.build());
@@ -255,10 +295,9 @@ public class InstructionUtils {
 	 * Convert an OFInstructionClearActions to string form. The string will be formatted
 	 * in a dpctl/ofctl-style syntax.
 	 * @param inst; The instruction to convert to a string
-	 * @param log
 	 * @return
 	 */
-	public static String clearActionsToString(OFInstructionClearActions inst, Logger log) {
+	public static String clearActionsToString(OFInstructionClearActions inst) {
 		return ""; // No data for this instruction. The presence of it's key indicates it is to be applied.
 	}
 
@@ -269,9 +308,8 @@ public class InstructionUtils {
 	 * side effect is the addition of an instruction in the OFFlowMod.Builder.
 	 * @param fmb; The FMB in which to append the new instruction
 	 * @param instStr; The string to parse the instruction from
-	 * @param log
 	 */
-	public static void clearActionsFromString(OFFlowMod.Builder fmb, String inst, Logger log) {
+	public static void clearActionsFromString(OFFlowMod.Builder fmb, String inst) {
 
 		if (fmb.getVersion().compareTo(OFVersion.OF_11) < 0) {
 			log.error("Clear Actions Instruction not supported in OpenFlow 1.0");
@@ -294,10 +332,9 @@ public class InstructionUtils {
 	 * Convert an OFInstructionMeter to string form. The string will be formatted
 	 * in a dpctl/ofctl-style syntax.
 	 * @param inst; The instruction to convert to a string
-	 * @param log
 	 * @return
 	 */
-	public static String meterToString(OFInstructionMeter inst, Logger log) {
+	public static String meterToString(OFInstructionMeter inst) {
 		return Long.toString(inst.getMeterId());
 	}
 
@@ -308,9 +345,8 @@ public class InstructionUtils {
 	 * side effect is the addition of an instruction in the OFFlowMod.Builder.
 	 * @param fmb; The FMB in which to append the new instruction
 	 * @param instStr; The string to parse the instruction from
-	 * @param log
 	 */
-	public static void meterFromString(OFFlowMod.Builder fmb, String inst, Logger log) {
+	public static void meterFromString(OFFlowMod.Builder fmb, String inst) {
 		if (inst == null || inst.isEmpty()) {
 			return;
 		}
@@ -322,11 +358,7 @@ public class InstructionUtils {
 
 		OFInstructionMeter.Builder ib = OFFactories.getFactory(fmb.getVersion()).instructions().buildMeter();
 
-		if (inst.startsWith("0x")) {
-			ib.setMeterId(Long.valueOf(inst.replaceFirst("0x", ""), 16));
-		} else {
-			ib.setMeterId(Long.valueOf(inst));
-		}		
+		ib.setMeterId(ParseUtils.parseHexOrDecLong(inst));		
 
 		log.debug("Appending (Goto)Meter instruction: {}", ib.build());
 		appendInstruction(fmb, ib.build());
@@ -339,10 +371,9 @@ public class InstructionUtils {
 	 * Convert an OFInstructionExperimenter to string form. The string will be formatted
 	 * in a dpctl/ofctl-style syntax.
 	 * @param inst; The instruction to convert to a string
-	 * @param log
 	 * @return
 	 */
-	public static String experimenterToString(OFInstructionExperimenter inst, Logger log) {
+	public static String experimenterToString(OFInstructionExperimenter inst) {
 		return Long.toString(inst.getExperimenter());
 	}
 
@@ -353,33 +384,265 @@ public class InstructionUtils {
 	 * side effect is the addition of an instruction in the OFFlowMod.Builder.
 	 * @param fmb; The FMB in which to append the new instruction
 	 * @param instStr; The string to parse the instruction from
-	 * @param log
 	 */
-	public static void experimenterFromString(OFFlowMod.Builder fmb, String inst, Logger log) {
-		/* TODO This is a no-op right now. */
+	public static void experimenterFromString(OFFlowMod.Builder fmb, String inst) {
+		log.warn("OFInstructionExperimenter from-string not implemented");
+	}
 
-		/*
-		if (inst == null || inst.equals("")) {
-			return; // TODO @Ryan quietly fail?
+	///////////////////////////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Convert an deprecated (OF1.5+) OFInstructionMeter to string form.
+	 * @param inst; The instruction to convert to a string
+	 * @return
+	 */
+	public static String deprecatedToString(OFInstruction inst) {
+		log.warn("OFInstructionDeprecated is being used. Did you mean to use OF1.5+ OFActionMeter?");
+		return "";
+	}
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Convert JSON string OFInstructionStatTrigger object.
+	 * @param fmb; Where the instruction will be placed
+	 * @param json; The string to convert to an OFInstructionStatTrigger
+	 * @return
+	 */
+	public static boolean statTriggerFromJsonString(OFFlowMod.Builder fmb, String json) {
+
+		OFInstructionStatTrigger i = statTriggerFromJsonString(fmb.getVersion(), json);
+		if (i != null) {
+			appendInstruction(fmb, i);
+			return true;
+		} else {
+			return false;
 		}
+	}
 
-		OFInstructionExperimenter.Builder ib = OFFactories.getFactory(fmb.getVersion()).instructions().buildExperimenter();
+	/**
+	 * Convert JSON string OFInstructionStatTrigger object.
+	 * @param json; The string to convert to an OFInstructionStatTrigger
+	 * @return
+	 */
+	public static OFInstructionStatTrigger statTriggerFromJsonString(OFVersion v, String json) {
+		if (json == null) {
+			throw new IllegalArgumentException("JSON string cannot be null");
+		}
 
-		String[] keyValue = inst.split("=");	
-		if (keyValue.length != 2) {
-			throw new IllegalArgumentException("[Key, Value] " + keyValue + " does not have form 'key=value' parsing " + inst);
+		final Set<OFStatTriggerFlags> flags = new HashSet<OFStatTriggerFlags>();
+		final Set<OFOxs<?>> thresholds = new HashSet<OFOxs<?>>();
+		final JsonParser jp;
+		try {
+			jp = jsonFactory.createParser(json);
+		} catch (IOException e) {
+			log.error("Could not create JSON parser for OFFlowMod.Builder {}", json);
+			return null;
 		}
-		switch (keyValue[0]) {
-		case STR_SUB_GOTO_METER_METER_ID:
-			ib.setExperimenter(Long.parseLong(keyValue[1]));
-			break;
-		default:
-			log.error("Invalid String key for OFInstructionExperimenter: {}", keyValue[0]);
+		try {
+			if (jp.nextToken() != JsonToken.START_OBJECT) {
+				throw new IOException("Expected START_OBJECT");
+			}
+
+			while (jp.nextToken() != JsonToken.END_OBJECT) {
+				String key = jp.getCurrentName().toLowerCase().trim();
+				if (jp.nextToken() != JsonToken.START_ARRAY) {
+					throw new IOException("Expected START_ARRAY");
+				}
+				switch (key) {
+				case "flags":
+					while (jp.nextToken() != JsonToken.END_ARRAY) {
+						boolean flagSet = false;
+						for (OFStatTriggerFlags f : OFStatTriggerFlags.values()) {
+							if (f.toString().equalsIgnoreCase(jp.getText().trim())) { /* case-insensitive, unlike enum.valueOf() */
+								flags.add(f);
+								flagSet = true;
+								break;
+							}
+						}
+						if (!flagSet) {
+							log.warn("Unknown flag {}", jp.getText());
+						}
+					}
+					break;
+				case "thresholds":
+					while (jp.nextToken() != JsonToken.END_ARRAY) {
+						if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+							throw new IOException("Expected START_OBJECT");
+						}
+						OFOxs.Builder<?> threshold = null;
+						U64 value = null;
+						while (jp.nextToken() != JsonToken.END_OBJECT) {
+							switch (jp.getCurrentName().toLowerCase().trim()) {
+							case "oxs_type":
+								String type = jp.getText().toLowerCase().trim();
+								if (type.equals(StatField.BYTE_COUNT.getName())) {
+									threshold = OFFactories.getFactory(v).oxss().buildByteCount();
+								} else if (type.equals(StatField.DURATION.getName())) {
+									threshold = OFFactories.getFactory(v).oxss().buildDuration();
+								} else if (type.equals(StatField.FLOW_COUNT.getName())) {
+									threshold = OFFactories.getFactory(v).oxss().buildFlowCount();
+								} else if (type.equals(StatField.IDLE_TIME.getName())) {
+									threshold = OFFactories.getFactory(v).oxss().buildIdleTime();
+								} else if (type.equals(StatField.PACKET_COUNT.getName())) {
+									threshold = OFFactories.getFactory(v).oxss().buildPacketCount();
+								} else {
+									log.warn("Unexpected OXS threshold type {}", type);
+								}
+								break;
+							case "value":
+								value = U64.of(ParseUtils.parseHexOrDecLong(jp.getText()));
+								break;
+							default:
+								log.warn("Unexpected OXS threshold key {}", jp.getCurrentName());
+								break;
+							}
+						}
+						if (threshold == null || value == null) {
+							log.error("Must specify both OXS type and threshold value. Got {} and {}, respectively", threshold, value);
+							return null;
+						} else {
+							if (threshold.getStatField().getName().equals(StatField.BYTE_COUNT.getName())) {
+								thresholds.add(((OFOxsByteCount.Builder) threshold).setValue(value).build());
+							} else if (threshold.getStatField().getName().equals(StatField.DURATION.getName())) {
+								thresholds.add(((OFOxsDuration.Builder) threshold).setValue(value).build());
+							} else if (threshold.getStatField().getName().equals(StatField.FLOW_COUNT.getName())) {
+								thresholds.add(((OFOxsFlowCount.Builder) threshold).setValue(U32.of(value.getValue())).build());
+							} else if (threshold.getStatField().getName().equals(StatField.IDLE_TIME.getName())) {
+								thresholds.add(((OFOxsIdleTime.Builder) threshold).setValue(value).build());
+							} else if (threshold.getStatField().getName().equals(StatField.PACKET_COUNT.getName())) {
+								thresholds.add(((OFOxsPacketCount.Builder) threshold).setValue(value).build());
+							} else {
+								log.warn("Unexpected OXS threshold type {}", threshold.getStatField().getName());
+							}
+						}
+					}
+					break;
+				default:
+					log.warn("Unexpected OFInstructionStatTrigger key {}", key);
+					break;
+				}
+			}
+		} catch (IOException e) {
+			log.error("Could not parse: {}", json);
+			log.error("JSON parse error message: {}", e.getMessage());
+			return null;
 		}
+		return OFFactories.getFactory(v).instructions().statTrigger(flags, OFOxsList.ofList(thresholds));
+	}
 
-		appendInstruction(fmb, ib.build());
-		 */
+	/**
+	 * Append OFInstructionStatsTrigger object to an existing JsonGenerator.
+	 * This method assumes the field name of the instruction has been
+	 * written already, if required. The appended data will
+	 * be formatted as follows:
+	 *   {
+	 *     "flags":[
+	 *       f1, f2, f3, ..., fn
+	 *     ],
+	 *     "thresholds":[
+	 *       {
+	 *       	"oxs_type":"l",
+	 *       	"value":"v"
+	 *       },
+	 *       {
+	 *       	"oxs_type":"m",
+	 *       	"value":"v"
+	 *       },
+	 *       ...,
+	 *       {
+	 *       	"oxs_type":"n",
+	 *       	"value":"v"
+	 *       }
+	 *     ]
+	 *   }
+	 * @param jsonGen
+	 * @param s
+	 */
+	public static void statTriggerToJsonString(JsonGenerator jsonGen, OFInstructionStatTrigger s) {
+		jsonGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);
+
+		try {
+			jsonGen.writeStartObject();
+			jsonGen.writeArrayFieldStart("flags");
+			for (OFStatTriggerFlags f : s.getFlags()) {
+				jsonGen.writeString(f.toString());
+			}
+			jsonGen.writeEndArray();
+			jsonGen.writeArrayFieldStart("thresholds");
+			for (OFOxs<?> o : s.getThresholds()) {
+				jsonGen.writeStartObject();
+				if (o instanceof OFOxsDuration) {
+					OFOxsDuration t = (OFOxsDuration) o; 
+					jsonGen.writeStringField("oxs_type", t.getStatField().getName());
+					jsonGen.writeStringField("value", t.getValue().toString());
+				} else if (o instanceof OFOxsByteCount) {
+					OFOxsByteCount t = (OFOxsByteCount) o;
+					jsonGen.writeStringField("oxs_type", t.getStatField().getName());
+					jsonGen.writeStringField("value", t.getValue().toString());
+				} else if (o instanceof OFOxsFlowCount) {
+					OFOxsFlowCount t = (OFOxsFlowCount) o;
+					jsonGen.writeStringField("oxs_type", t.getStatField().getName());
+					jsonGen.writeStringField("value", t.getValue().toString());
+				} else if (o instanceof OFOxsIdleTime) {
+					OFOxsIdleTime t = (OFOxsIdleTime) o;
+					jsonGen.writeStringField("oxs_type", t.getStatField().getName());
+					jsonGen.writeStringField("value", t.getValue().toString());
+				} else if (o instanceof OFOxsPacketCount) {
+					OFOxsPacketCount t = (OFOxsPacketCount) o;
+					jsonGen.writeStringField("oxs_type", t.getStatField().getName());
+					jsonGen.writeStringField("value", t.getValue().toString());
+				} else {
+					log.warn("Skipping unknown OXS type {}", o.getStatField().getName());
+				}
+				jsonGen.writeEndObject();
+			}
+			jsonGen.writeEndArray();
+			jsonGen.close();
+		} catch (IOException e) {
+			log.error("Error composing OFInstructionStatTrigger JSON object. {}", e.getMessage());
+		}
 	}
 
+	/**
+	 * Create OFInstructionStatsTrigger JSON object string.
+	 * This method assumes the field name of the instruction will be
+	 * written externally, if required. The appended JSON string will
+	 * be formatted as follows:
+	 *   {
+	 *     "flags":[
+	 *       f1, f2, f3, ..., fn
+	 *     ],
+	 *     "thresholds":[
+	 *       {
+	 *       	"oxs_type":"l",
+	 *       	"value":"v"
+	 *       },
+	 *       {
+	 *       	"oxs_type":"m",
+	 *       	"value":"v"
+	 *       },
+	 *       ...,
+	 *       {
+	 *       	"oxs_type":"n",
+	 *       	"value":"v"
+	 *       }
+	 *     ]
+	 *   }
+	 * @param s
+	 */
+	public static String statTriggerToJsonString(OFInstructionStatTrigger s) {
+		Writer w = new StringWriter();
+		JsonGenerator jsonGen;
+		try {
+			jsonGen = jsonFactory.createGenerator(w);
+		} catch (IOException e) {
+			log.error("Could not instantiate JSON Generator. {}", e.getMessage());
+			return JSON_EMPTY_OBJECT;
+		}
+
+		statTriggerToJsonString(jsonGen, s);
 
-}
+		return w.toString(); /* overridden impl returns contents of Writer's StringBuffer */
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/MatchUtils.java b/src/main/java/net/floodlightcontroller/util/MatchUtils.java
index 73d8db53618af52b1f7b1d4e3089e410f036e54a..8bcd39226a1f9e5e3126b5cfeb00253549a95fc5 100644
--- a/src/main/java/net/floodlightcontroller/util/MatchUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/MatchUtils.java
@@ -1,12 +1,18 @@
 package net.floodlightcontroller.util;
 
+import java.lang.reflect.Field;
 import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
 
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
 import org.projectfloodlight.openflow.types.ArpOpcode;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.ICMPv4Code;
@@ -21,17 +27,22 @@ import org.projectfloodlight.openflow.types.IpProtocol;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBooleanValue;
 import org.projectfloodlight.openflow.types.OFMetadata;
-import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
 import org.projectfloodlight.openflow.types.OFVlanVidMatchWithMask;
+import org.projectfloodlight.openflow.types.PacketType;
 import org.projectfloodlight.openflow.types.TransportPort;
+import org.projectfloodlight.openflow.types.U16;
 import org.projectfloodlight.openflow.types.U32;
 import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.U8;
 import org.projectfloodlight.openflow.types.VlanPcp;
+import org.projectfloodlight.openflow.types.OFPort;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
 /**
  * Utilities for working with Matches. Includes workarounds for
  * current Loxi limitations/bugs. 
@@ -57,7 +68,7 @@ public class MatchUtils {
 	 * expect the JSON string to be formatted using these strings for the applicable fields.
 	 */
 	public static final String STR_IN_PORT = "in_port";
-	
+
 	/* These are special port IDs */
 	public static final String STR_PORT_LOCAL = "local";
 	public static final String STR_PORT_CONTROLLER = "controller";
@@ -66,6 +77,9 @@ public class MatchUtils {
 	public static final String STR_PORT_FLOOD = "flood";
 	public static final String STR_PORT_NORMAL = "normal";
 	public static final String STR_PORT_TABLE = "table";
+	public static final String STR_PORT_MAX = "max";
+	public static final String STR_PORT_ANY = "any";
+
 
 	public static final String STR_IN_PHYS_PORT = "in_phys_port";
 
@@ -80,9 +94,10 @@ public class MatchUtils {
 	public static final String STR_IPV6_DST = "ipv6_dst";
 	public static final String STR_IPV6_SRC = "ipv6_src";
 	public static final String STR_IPV6_FLOW_LABEL = "ipv6_label";
-	public static final String STR_IPV6_ND_SSL = "ipv6_nd_ssl";
+	public static final String STR_IPV6_EXTHDR = "ipv6_exthdr";
+	public static final String STR_IPV6_ND_SLL = "ipv6_nd_sll";
 	public static final String STR_IPV6_ND_TARGET = "ipv6_nd_target";
-	public static final String STR_IPV6_ND_TTL = "ipv6_nd_ttl";
+	public static final String STR_IPV6_ND_TLL = "ipv6_nd_tll";
 	public static final String STR_NW_PROTO = "ip_proto";
 	public static final String STR_NW_TOS = "ip_tos";
 	public static final String STR_NW_ECN = "ip_ecn";
@@ -94,7 +109,7 @@ public class MatchUtils {
 	public static final String STR_UDP_SRC = "udp_src";
 	public static final String STR_TCP_DST = "tcp_dst";
 	public static final String STR_TCP_SRC = "tcp_src";
-	public static final String STR_TP_DST = "tp_dst"; // support for OF1.0 generic transport ports (possibly sent from the rest api). Only use these to read them in, but store them as the type of port their IpProto is set to.
+	public static final String STR_TP_DST = "tp_dst"; // support for OF1.0 generic transport ports
 	public static final String STR_TP_SRC = "tp_src";
 
 	public static final String STR_ICMP_TYPE = "icmpv4_type";
@@ -117,10 +132,272 @@ public class MatchUtils {
 	public static final String STR_TUNNEL_IPV4_SRC = "tunnel_ipv4_src";
 	public static final String STR_TUNNEL_IPV4_DST = "tunnel_ipv4_dst";
 
-	public static final String STR_PBB_ISID = "pbb_isid";	
+	public static final String STR_PBB_ISID = "pbb_isid"; //TODO support when Loxi does
+	public static final String STR_PBB_UCA = "pbb_uca";
+
+	public static final String STR_TCP_FLAGS = "tcp_flags";
+	public static final String STR_ACTSET_OUTPUT = "actset_output";
+	public static final String STR_PACKET_TYPE = "packet_type";
 
 	public static final String SET_FIELD_DELIM = "->";
 
+	private static Map<MatchFields, MatchField<?>> ALL_MATCH_FIELDS;
+	private static Map<MatchFields, Set<OFVersion>> SUPPORTED_OFVERSIONS;
+
+	private static final Match.Builder v10 = OFFactories.getFactory(OFVersion.OF_10).buildMatch();
+	private static final Match.Builder v11 = OFFactories.getFactory(OFVersion.OF_11).buildMatch();
+	private static final Match.Builder v12 = OFFactories.getFactory(OFVersion.OF_12).buildMatch();
+	private static final Match.Builder v13 = OFFactories.getFactory(OFVersion.OF_13).buildMatch();
+	private static final Match.Builder v14 = OFFactories.getFactory(OFVersion.OF_14).buildMatch();
+	private static final Match.Builder v15 = OFFactories.getFactory(OFVersion.OF_15).buildMatch();
+
+	private static void initGlobals() {
+		Map<MatchFields, MatchField<?>> tmpAll = new HashMap<MatchFields, MatchField<?>>();
+		Map<MatchFields, Set<OFVersion>> tmpVer = new HashMap<MatchFields, Set<OFVersion>>();
+		for (Field field : MatchField.ARP_OP.getClass().getFields()) {
+			if (log.isTraceEnabled()) {
+				log.trace("Checking MatchField {}", field.getName());
+			}
+			if (field.getType() == MatchField.class) {
+				try {
+					/* null b/c static final field we're trying to access */
+					MatchField<?> f = (MatchField<?>) field.get(null);
+					synchronized (MatchUtils.class) {
+						tmpAll.put(f.id, f); /* !MUST! set this before SUPPORTED_OFVERSIONS */
+						tmpVer.put(f.id, getSupportedOFVersions(f.id));
+					}
+					if (log.isDebugEnabled()) {
+						log.debug("Added MatchField {} to list of all MatchFields", f.getName());
+					}
+				} catch (IllegalArgumentException | IllegalAccessException e) {
+					log.error("Could not add MatchField {} to list of all MatchFields", field.getName());
+				}
+			}
+		}
+		ALL_MATCH_FIELDS = ImmutableMap.copyOf(tmpAll);
+		SUPPORTED_OFVERSIONS = ImmutableMap.copyOf(tmpVer);
+	}
+
+	public static Set<OFVersion> getSupportedOFVersions(MatchFields m) {
+		if (ALL_MATCH_FIELDS == null || SUPPORTED_OFVERSIONS == null) {
+			initGlobals();
+		}
+
+		Set<OFVersion> v = SUPPORTED_OFVERSIONS.get(m);
+		if (v != null) {
+			return v; /* should already be unmodifiable */
+		}
+
+		v = new HashSet<OFVersion>();
+		if (v10.supports(getMatchField(m))) {
+			v.add(OFVersion.OF_10);
+		}
+		if (v11.supports(getMatchField(m))) {
+			v.add(OFVersion.OF_11);
+		}
+		if (v12.supports(getMatchField(m))) {
+			v.add(OFVersion.OF_12);
+		}
+		if (v13.supports(getMatchField(m))) {
+			v.add(OFVersion.OF_13);
+		}
+		if (v14.supports(getMatchField(m))) {
+			v.add(OFVersion.OF_14);
+		}
+		if (v15.supports(getMatchField(m))) {
+			v.add(OFVersion.OF_15);
+		}
+
+		SUPPORTED_OFVERSIONS.put(m, ImmutableSet.copyOf(v));
+		return SUPPORTED_OFVERSIONS.get(m);
+	}
+
+	public static MatchField<?> getMatchField(MatchFields m) {
+		if (ALL_MATCH_FIELDS == null || SUPPORTED_OFVERSIONS == null) {
+			initGlobals();
+		}
+		return ALL_MATCH_FIELDS.get(m);
+	}
+
+	/**
+	 * Get the string name of a MatchField as it's exposed through
+	 * the REST API of Floodlight. This is, unfortunately, not the
+	 * same name as the ones used internally within MatchField.
+	 * @param mf
+	 * @return
+	 */
+	public static String getMatchFieldName(MatchFields mf) {
+		String key = "";
+		switch (mf) {
+		case ARP_OP:
+			key = STR_ARP_OPCODE;
+			break;
+		case ARP_SHA:
+			key = STR_ARP_SHA;
+			break;
+		case ARP_SPA:
+			key = STR_ARP_SPA;
+			break;
+		case ARP_THA:
+			key = STR_ARP_DHA;
+			break;
+		case ARP_TPA:
+			key = STR_ARP_DPA;
+			break;
+		case ETH_DST:
+			key = STR_DL_DST;
+			break;
+		case ETH_SRC:
+			key = STR_DL_SRC;
+			break;
+		case ETH_TYPE:
+			key = STR_DL_TYPE;
+			break;
+		case ICMPV4_CODE:
+			key = STR_ICMP_CODE;
+			break;
+		case ICMPV4_TYPE:
+			key = STR_ICMP_TYPE;
+			break;
+		case ICMPV6_CODE:
+			key = STR_ICMPV6_CODE;
+			break;
+		case ICMPV6_TYPE:
+			key = STR_ICMPV6_TYPE;
+			break;
+		case IN_PHY_PORT:
+			key = STR_IN_PHYS_PORT;
+			break;
+		case IN_PORT:
+			key = STR_IN_PORT;
+			break;
+		case IPV4_DST:
+			key = STR_NW_DST;
+			break;
+		case IPV4_SRC:
+			key = STR_NW_SRC;
+			break;
+		case IPV6_DST:
+			key = STR_IPV6_DST;
+			break;
+		case IPV6_EXTHDR:
+			key = STR_IPV6_EXTHDR;
+			break;
+		case IPV6_FLABEL:
+			key = STR_IPV6_FLOW_LABEL;
+			break;
+		case IPV6_ND_SLL:
+			key = STR_IPV6_ND_SLL;
+			break;
+		case IPV6_ND_TARGET:
+			key = STR_IPV6_ND_TARGET;
+			break;
+		case IPV6_ND_TLL:
+			key = STR_IPV6_ND_TLL;
+			break;
+		case IPV6_SRC:
+			key = STR_IPV6_SRC;
+			break;
+		case IP_DSCP:
+			key = STR_NW_DSCP;
+			break;
+		case IP_ECN:
+			key = STR_NW_ECN;
+			break;
+		case IP_PROTO:
+			key = STR_NW_PROTO;
+			break;
+		case METADATA:
+			key = STR_METADATA;
+			break;
+		case MPLS_BOS:
+			key = STR_MPLS_BOS;
+			break;
+		case MPLS_LABEL:
+			key = STR_MPLS_LABEL;
+			break;
+		case MPLS_TC:
+			key = STR_MPLS_TC;
+			break;
+		case PBB_UCA:
+			key = STR_PBB_UCA;
+			break;
+		case SCTP_DST:
+			key = STR_SCTP_DST;
+			break;
+		case SCTP_SRC:
+			key = STR_SCTP_SRC;
+			break;
+		case TCP_DST:
+			key = STR_TCP_DST;
+			break;
+		case TCP_SRC:
+			key = STR_TCP_SRC;
+			break;
+		case TUNNEL_ID:
+			key = STR_TUNNEL_ID;
+			break;
+		case TUNNEL_IPV4_DST:
+			key = STR_TUNNEL_IPV4_DST;
+			break;
+		case TUNNEL_IPV4_SRC:
+			key = STR_TUNNEL_IPV4_SRC;
+			break;
+		case UDP_DST:
+			key = STR_UDP_DST;
+			break;
+		case UDP_SRC:
+			key = STR_UDP_SRC;
+			break;
+		case VLAN_PCP:
+			key = STR_DL_VLAN_PCP;
+			break;
+		case VLAN_VID:
+			key = STR_DL_VLAN;
+			break;
+		case PACKET_TYPE:
+			key = STR_PACKET_TYPE;
+			break;
+		case TCP_FLAGS:
+			key = STR_TCP_FLAGS;
+			break;
+		case ACTSET_OUTPUT:
+			key = STR_ACTSET_OUTPUT;
+			break;
+		/* NOTE: keep BSN MatchFields to eliminate need for default case.
+		   Unaccounted for fields will then produce warning in future */
+		case BSN_EGR_PORT_GROUP_ID: 
+		case BSN_GLOBAL_VRF_ALLOWED:
+		case BSN_INGRESS_PORT_GROUP_ID:
+		case BSN_INNER_ETH_DST:
+		case BSN_INNER_ETH_SRC:
+		case BSN_INNER_VLAN_VID:
+		case BSN_IN_PORTS_128:
+		case BSN_IN_PORTS_512:
+		case BSN_L2_CACHE_HIT:
+		case BSN_L3_DST_CLASS_ID:
+		case BSN_L3_INTERFACE_CLASS_ID:
+		case BSN_L3_SRC_CLASS_ID:
+		case BSN_LAG_ID:
+		case BSN_TCP_FLAGS:
+		case BSN_UDF0:
+		case BSN_UDF1:
+		case BSN_UDF2:
+		case BSN_UDF3:
+		case BSN_UDF4:
+		case BSN_UDF5:
+		case BSN_UDF6:
+		case BSN_UDF7:
+		case BSN_VFI:
+		case BSN_VLAN_XLATE_PORT_GROUP_ID:
+		case BSN_VRF:
+		case BSN_VXLAN_NETWORK_ID:
+			log.warn("Ignoring BSN MatchField {}", mf);
+			break;
+		}
+		return key;
+	}
+
 	/**
 	 * Create a point-to-point match for two devices at the IP layer.
 	 * Takes an existing match (e.g. from a PACKET_IN), and masks all
@@ -196,7 +473,7 @@ public class MatchUtils {
 		 */
 		Match.Builder mb = OFFactories.getFactory(version).buildMatch(); 
 		Iterator<MatchField<?>> itr = parent.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded)
-		while(itr.hasNext()) {
+		while (itr.hasNext()) {
 			@SuppressWarnings("rawtypes")
 			MatchField mf = itr.next();
 			if (parent.isExact(mf)) {
@@ -259,69 +536,12 @@ public class MatchUtils {
 	}
 
 	/**
-	 * TODO NOT IMPLEMENTED! (Marked as Deprecated for the time being.)
+	 * TODO NOT IMPLEMENTED!
 	 * 
 	 * Returns empty string right now.
-	 * Output a dpctl-styled string, i.e., only list the elements that are not wildcarded.
-	 * 
-	 * A match-everything Match outputs "Match[]"
-	 * 
-	 * @return "Match[dl_src:00:20:01:11:22:33,nw_src:192.168.0.0/24,tp_dst:80]"
 	 */
 	@Deprecated
 	public static String toString(Match match) {
-		/*String str = "";
-
-	        match
-
-	        // l1
-	        if ((wildcards & OFPFW_IN_PORT) == 0)
-	            str += "," + STR_IN_PORT + "=" + U16.f(this.inputPort);
-
-	        // l2
-	        if ((wildcards & OFPFW_DL_DST) == 0)
-	            str += "," + STR_DL_DST + "="
-	                    + match.);
-	        if ((wildcards & OFPFW_DL_SRC) == 0)
-	            str += "," + STR_DL_SRC + "="
-	                    + HexString.toHexString(this.dataLayerSource);
-	        if ((wildcards & OFPFW_DL_TYPE) == 0)
-	            str += "," + STR_DL_TYPE + "=0x"
-	                    + Integer.toHexString(U16.f(this.dataLayerType));
-	        if ((wildcards & OFPFW_DL_VLAN) == 0)
-	            str += "," + STR_DL_VLAN + "=0x"
-	                    + Integer.toHexString(U16.f(this.dataLayerVirtualLan));
-	        if ((wildcards & OFPFW_DL_VLAN_PCP) == 0)
-	            str += ","
-	                    + STR_DL_VLAN_PCP
-	                    + "="
-	                    + Integer.toHexString(U8
-	                            .f(this.dataLayerVirtualLanPriorityCodePoint));
-
-	        // l3
-	        if (getNetworkDestinationMaskLen() > 0)
-	            str += ","
-	                    + STR_NW_DST
-	                    + "="
-	                    + cidrToString(networkDestination,
-	                            getNetworkDestinationMaskLen());
-	        if (getNetworkSourceMaskLen() > 0)
-	            str += "," + STR_NW_SRC + "="
-	                    + cidrToString(networkSource, getNetworkSourceMaskLen());
-	        if ((wildcards & OFPFW_NW_PROTO) == 0)
-	            str += "," + STR_NW_PROTO + "=" + U8.f(this.networkProtocol);
-	        if ((wildcards & OFPFW_NW_TOS) == 0)
-	            str += "," + STR_NW_TOS + "=" + U8.f(this.networkTypeOfService);
-
-	        // l4
-	        if ((wildcards & OFPFW_TP_DST) == 0)
-	            str += "," + STR_TP_DST + "=" + U16.f(this.transportDestination);
-	        if ((wildcards & OFPFW_TP_SRC) == 0)
-	            str += "," + STR_TP_SRC + "=" + U16.f(this.transportSource);
-	        if ((str.length() > 0) && (str.charAt(0) == ','))
-	            str = str.substring(1); // trim the leading ","
-	        // done
-	        return "OFMatch[" + str + "]"; */
 		return "";
 	}
 
@@ -331,38 +551,6 @@ public class MatchUtils {
 	 * dpctl-style string, e.g., from the output of OFMatch.toString(). The
 	 * exact syntax for each key is defined by the string constants at the top
 	 * of MatchUtils.java. <br>
-	 * <p>
-	 * Supported keys/values include <br>
-	 * <p>
-	 * <TABLE border=1>
-	 * <TR>
-	 * <TD>KEY(s)
-	 * <TD>VALUE
-	 * </TR>
-	 * <TR>
-	 * <TD>"in_port"
-	 * <TD>integer
-	 * </TR>
-	 * <TR>
-	 * <TD>"eth_src", "eth_dst"
-	 * <TD>hex-string
-	 * </TR>
-	 * <TR>
-	 * <TD>"eth_type", "eth_vlan_vid", "eth_vlan_pcp"
-	 * <TD>integer
-	 * </TR>
-	 * <TR>
-	 * <TD>"ipv4_src", "ipv4_dst"
-	 * <TD>CIDR-style netmask
-	 * </TR>
-	 * <TR>
-	 * <TD>"tp_src","tp_dst", "tcp_src", "tcp_dst", "udp_src", "udp_dst", etc.
-	 * <TD>integer (max 64k)
-	 * </TR>
-	 * </TABLE>
-	 * <p>
-	 * The CIDR-style netmasks assume 32 netmask if none given, so:
-	 * "128.8.128.118/32" is the same as "128.8.128.118"
 	 * 
 	 * @param match
 	 *            a key=value comma separated string, e.g.
@@ -426,16 +614,9 @@ public class MatchUtils {
 			switch (key_value[0]) {
 			case STR_IN_PORT:
 				if (dataMask.length == 1) {
-					if (dataMask[0].equals(STR_PORT_LOCAL)) {
-						mb.setExact(MatchField.IN_PORT, OFPort.LOCAL);
-					} else if (dataMask[0].equals(STR_PORT_CONTROLLER)) {
-						mb.setExact(MatchField.IN_PORT, OFPort.CONTROLLER);
-					} else {
-						mb.setExact(MatchField.IN_PORT, OFPort.of(U32.of(dataMask[0].contains("0x") ? Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[0])).getRaw()));
-					}
+				    mb.setExact(MatchField.IN_PORT, portFromString(dataMask[0]));
 				} else {
-					mb.setMasked(MatchField.IN_PORT, OFPort.of(U32.of(dataMask[0].contains("0x") ? Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[0])).getRaw()), 
-							OFPort.of(U32.of(dataMask[1].contains("0x") ? Long.parseLong(dataMask[1].replaceFirst("0x", ""), 16) : Long.parseLong(dataMask[1])).getRaw()));
+					mb.setMasked(MatchField.IN_PORT, portFromString(dataMask[0]), portFromString(dataMask[1]));
 				}
 				break;
 			case STR_DL_DST: /* Only accept hex-string for MAC addresses */
@@ -454,27 +635,27 @@ public class MatchUtils {
 				break;
 			case STR_DL_TYPE:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.ETH_TYPE, EthType.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+					mb.setExact(MatchField.ETH_TYPE, EthType.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.ETH_TYPE, EthType.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-							EthType.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+					mb.setMasked(MatchField.ETH_TYPE, EthType.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+							EthType.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 				}
 				break;
 			case STR_DL_VLAN:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofRawVid(dataMask[0].contains("0x") ? Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[0])));
+					mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofRawVid(ParseUtils.parseHexOrDecShort(dataMask[0])));
 				} else {
 					mb.setMasked(MatchField.VLAN_VID, OFVlanVidMatchWithMask.of(
-							OFVlanVidMatch.ofRawVid(dataMask[0].contains("0x") ? Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[0])), 
-							OFVlanVidMatch.ofRawVid(dataMask[1].contains("0x") ? Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[1]))));
+							OFVlanVidMatch.ofRawVid(ParseUtils.parseHexOrDecShort(dataMask[0])), 
+							OFVlanVidMatch.ofRawVid(ParseUtils.parseHexOrDecShort(dataMask[1]))));
 				}
 				break;
 			case STR_DL_VLAN_PCP:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.VLAN_PCP, VlanPcp.of(dataMask[0].contains("0x") ? U8.t(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[0]))));
+					mb.setExact(MatchField.VLAN_PCP, VlanPcp.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[0]))));
 				} else {
-					mb.setMasked(MatchField.VLAN_PCP, VlanPcp.of(dataMask[0].contains("0x") ? U8.t(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[0]))), 
-							VlanPcp.of(dataMask[1].contains("0x") ? U8.t(Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[1]))));
+					mb.setMasked(MatchField.VLAN_PCP, VlanPcp.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[0]))), 
+							VlanPcp.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[1]))));
 				}
 				break;
 			case STR_NW_DST: /* Only accept dotted-decimal for IPv4 addresses */
@@ -500,45 +681,45 @@ public class MatchUtils {
 					throw new IllegalArgumentException("OF Version incompatible");
 				}
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.IPV6_FLABEL, IPv6FlowLabel.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+					mb.setExact(MatchField.IPV6_FLABEL, IPv6FlowLabel.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.IPV6_FLABEL, IPv6FlowLabel.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-							IPv6FlowLabel.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+					mb.setMasked(MatchField.IPV6_FLABEL, IPv6FlowLabel.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+							IPv6FlowLabel.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 				}
 				break;
 			case STR_NW_PROTO:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.of(dataMask[0].contains("0x") ? Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[0])));
+					mb.setExact(MatchField.IP_PROTO, IpProtocol.of(ParseUtils.parseHexOrDecShort(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.IP_PROTO, IpProtocol.of(dataMask[0].contains("0x") ? Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[0])), 
-							IpProtocol.of(dataMask[1].contains("0x") ? Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[1])));
+					mb.setMasked(MatchField.IP_PROTO, IpProtocol.of(ParseUtils.parseHexOrDecShort(dataMask[0])), 
+							IpProtocol.of(ParseUtils.parseHexOrDecShort(dataMask[1])));
 				}
 				break;
 			case STR_NW_TOS:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.IP_ECN, IpEcn.of(dataMask[0].contains("0x") ? U8.t(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[0]))));
-					mb.setExact(MatchField.IP_DSCP, IpDscp.of(dataMask[0].contains("0x") ? U8.t(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[0]))));
+					mb.setExact(MatchField.IP_ECN, IpEcn.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[0]))));
+					mb.setExact(MatchField.IP_DSCP, IpDscp.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[0]))));
 				} else {
-					mb.setMasked(MatchField.IP_ECN, IpEcn.of(dataMask[0].contains("0x") ? U8.t(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[0]))), 
-							IpEcn.of(dataMask[1].contains("0x") ? U8.t(Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[1]))));
-					mb.setMasked(MatchField.IP_DSCP, IpDscp.of(dataMask[0].contains("0x") ? U8.t(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[0]))), 
-							IpDscp.of(dataMask[1].contains("0x") ? U8.t(Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[1]))));
+					mb.setMasked(MatchField.IP_ECN, IpEcn.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[0]))), 
+							IpEcn.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[1]))));
+					mb.setMasked(MatchField.IP_DSCP, IpDscp.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[0]))), 
+							IpDscp.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[1]))));
 				}
 				break;
 			case STR_NW_ECN:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.IP_ECN, IpEcn.of(dataMask[0].contains("0x") ? U8.t(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[0]))));
+					mb.setExact(MatchField.IP_ECN, IpEcn.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[0]))));
 				} else {
-					mb.setMasked(MatchField.IP_ECN, IpEcn.of(dataMask[0].contains("0x") ? U8.t(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[0]))), 
-							IpEcn.of(dataMask[1].contains("0x") ? U8.t(Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[1]))));
+					mb.setMasked(MatchField.IP_ECN, IpEcn.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[0]))), 
+							IpEcn.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[1]))));
 				}
 				break;
 			case STR_NW_DSCP:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.IP_DSCP, IpDscp.of(dataMask[0].contains("0x") ? U8.t(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[0]))));
+					mb.setExact(MatchField.IP_DSCP, IpDscp.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[0]))));
 				} else {
-					mb.setMasked(MatchField.IP_DSCP, IpDscp.of(dataMask[0].contains("0x") ? U8.t(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[0]))), 
-							IpDscp.of(dataMask[1].contains("0x") ? U8.t(Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16)) : U8.t(Short.parseShort(dataMask[1]))));
+					mb.setMasked(MatchField.IP_DSCP, IpDscp.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[0]))), 
+							IpDscp.of(U8.t(ParseUtils.parseHexOrDecShort(dataMask[1]))));
 				}
 				break;
 			case STR_SCTP_DST: // for transport ports, if we don't know the transport protocol yet, postpone parsing this [key, value] pair until we know. Put it at the back of the queue.
@@ -546,10 +727,10 @@ public class MatchUtils {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.SCTP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.SCTP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.SCTP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.SCTP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				}
 				break;
@@ -558,10 +739,10 @@ public class MatchUtils {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.SCTP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.SCTP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.SCTP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.SCTP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				}
 				break;
@@ -570,10 +751,10 @@ public class MatchUtils {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.UDP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.UDP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.UDP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.UDP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				}
 				break;
@@ -582,10 +763,10 @@ public class MatchUtils {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.UDP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.UDP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.UDP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.UDP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				}
 				break;
@@ -594,10 +775,10 @@ public class MatchUtils {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.TCP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.TCP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.TCP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.TCP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				}
 				break;
@@ -606,10 +787,10 @@ public class MatchUtils {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else {
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.TCP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.TCP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.TCP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.TCP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				}
 				break;
@@ -618,24 +799,24 @@ public class MatchUtils {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				} else if (ipProto == IpProtocol.TCP){
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.TCP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.TCP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.TCP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.TCP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				} else if (ipProto == IpProtocol.UDP){
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.UDP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.UDP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.UDP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.UDP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				} else if (ipProto == IpProtocol.SCTP){
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.SCTP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.SCTP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.SCTP_DST, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.SCTP_DST, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				}
 				break;
@@ -644,41 +825,41 @@ public class MatchUtils {
 					llValues.add(key_value); // place it back if we can't proceed yet
 				}  else if (ipProto == IpProtocol.TCP){
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.TCP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.TCP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.TCP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.TCP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				} else if (ipProto == IpProtocol.UDP){
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.UDP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.UDP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.UDP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.UDP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				} else if (ipProto == IpProtocol.SCTP){
 					if (dataMask.length == 1) {
-						mb.setExact(MatchField.SCTP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])));
+						mb.setExact(MatchField.SCTP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 					} else {
-						mb.setMasked(MatchField.SCTP_SRC, TransportPort.of(dataMask[0].contains("0x") ? Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[0])), 
-								TransportPort.of(dataMask[1].contains("0x") ? Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16) : Integer.parseInt(dataMask[1])));
+						mb.setMasked(MatchField.SCTP_SRC, TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+								TransportPort.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 					}
 				}
 				break;
 			case STR_ICMP_TYPE:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.ICMPV4_TYPE, ICMPv4Type.of(dataMask[0].contains("0x") ? Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[0])));
+					mb.setExact(MatchField.ICMPV4_TYPE, ICMPv4Type.of(ParseUtils.parseHexOrDecShort(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.ICMPV4_TYPE, ICMPv4Type.of(dataMask[0].contains("0x") ? Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[0])), 
-							ICMPv4Type.of(dataMask[1].contains("0x") ? Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[1])));
+					mb.setMasked(MatchField.ICMPV4_TYPE, ICMPv4Type.of(ParseUtils.parseHexOrDecShort(dataMask[0])), 
+							ICMPv4Type.of(ParseUtils.parseHexOrDecShort(dataMask[1])));
 				}
 				break;
 			case STR_ICMP_CODE:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.ICMPV4_CODE, ICMPv4Code.of(dataMask[0].contains("0x") ? Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[0])));
+					mb.setExact(MatchField.ICMPV4_CODE, ICMPv4Code.of(ParseUtils.parseHexOrDecShort(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.ICMPV4_CODE, ICMPv4Code.of(dataMask[0].contains("0x") ? Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[0])), 
-							ICMPv4Code.of(dataMask[1].contains("0x") ? Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16) : Short.parseShort(dataMask[1])));
+					mb.setMasked(MatchField.ICMPV4_CODE, ICMPv4Code.of(ParseUtils.parseHexOrDecShort(dataMask[0])), 
+							ICMPv4Code.of(ParseUtils.parseHexOrDecShort(dataMask[1])));
 				}
 				break;
 			case STR_ICMPV6_TYPE:
@@ -686,10 +867,10 @@ public class MatchUtils {
 					throw new IllegalArgumentException("OF Version incompatible");
 				}
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.ICMPV6_TYPE, dataMask[0].contains("0x") ? U8.of(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.parseShort(dataMask[0])));
+					mb.setExact(MatchField.ICMPV6_TYPE, U8.of(ParseUtils.parseHexOrDecShort(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.ICMPV6_TYPE, dataMask[0].contains("0x") ? U8.of(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.parseShort(dataMask[0])), 
-							dataMask[1].contains("0x") ? U8.of(Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16)) : U8.of(Short.parseShort(dataMask[1])));
+					mb.setMasked(MatchField.ICMPV6_TYPE, U8.of(ParseUtils.parseHexOrDecShort(dataMask[0])), 
+					        U8.of(ParseUtils.parseHexOrDecShort(dataMask[1])));
 				}
 				break;
 			case STR_ICMPV6_CODE:
@@ -697,13 +878,13 @@ public class MatchUtils {
 					throw new IllegalArgumentException("OF Version incompatible");
 				}
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.ICMPV6_CODE, dataMask[0].contains("0x") ? U8.of(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.parseShort(dataMask[0])));
+					mb.setExact(MatchField.ICMPV6_CODE, U8.of(ParseUtils.parseHexOrDecShort(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.ICMPV6_CODE, dataMask[0].contains("0x") ? U8.of(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.parseShort(dataMask[0])), 
-							dataMask[1].contains("0x") ? U8.of(Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16)) : U8.of(Short.parseShort(dataMask[1])));
+					mb.setMasked(MatchField.ICMPV6_CODE, U8.of(ParseUtils.parseHexOrDecShort(dataMask[0])), 
+					    U8.of(ParseUtils.parseHexOrDecShort(dataMask[1])));
 				}
 				break;
-			case STR_IPV6_ND_SSL:
+			case STR_IPV6_ND_SLL:
 				if (ver10 == true) {
 					throw new IllegalArgumentException("OF Version incompatible");
 				}
@@ -713,7 +894,7 @@ public class MatchUtils {
 					mb.setMasked(MatchField.IPV6_ND_SLL, MacAddress.of(dataMask[0]), MacAddress.of(dataMask[1]));
 				}
 				break;
-			case STR_IPV6_ND_TTL:
+			case STR_IPV6_ND_TLL:
 				if (ver10 == true) {
 					throw new IllegalArgumentException("OF Version incompatible");
 				}
@@ -729,12 +910,20 @@ public class MatchUtils {
 				}
 				mb.setMasked(MatchField.IPV6_ND_TARGET, IPv6AddressWithMask.of(key_value[1]));
 				break;
+			case STR_IPV6_EXTHDR:
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.IPV6_EXTHDR, U16.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
+				} else {
+					mb.setMasked(MatchField.IPV6_EXTHDR, U16.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+					        U16.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
+				}
+				break;
 			case STR_ARP_OPCODE:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.ARP_OP, dataMask[0].contains("0x") ? ArpOpcode.of(Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16)) : ArpOpcode.of(Integer.parseInt(dataMask[0])));
+					mb.setExact(MatchField.ARP_OP, ArpOpcode.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.ARP_OP, dataMask[0].contains("0x") ? ArpOpcode.of(Integer.parseInt(dataMask[0].replaceFirst("0x", ""), 16)) : ArpOpcode.of(Integer.parseInt(dataMask[0])), 
-							dataMask[1].contains("0x") ? ArpOpcode.of(Integer.parseInt(dataMask[1].replaceFirst("0x", ""), 16)) : ArpOpcode.of(Integer.parseInt(dataMask[1])));
+					mb.setMasked(MatchField.ARP_OP, ArpOpcode.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+							ArpOpcode.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
 				}
 				break;
 			case STR_ARP_SHA:
@@ -759,37 +948,37 @@ public class MatchUtils {
 				break;
 			case STR_MPLS_LABEL:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.MPLS_LABEL, dataMask[0].contains("0x") ? U32.of(Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16)) : U32.of(Long.parseLong(dataMask[0])));
+					mb.setExact(MatchField.MPLS_LABEL, U32.of(ParseUtils.parseHexOrDecLong(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.MPLS_LABEL, dataMask[0].contains("0x") ? U32.of(Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16)) : U32.of(Long.parseLong(dataMask[0])), 
-							dataMask[1].contains("0x") ? U32.of(Long.parseLong(dataMask[1].replaceFirst("0x", ""), 16)) : U32.of(Long.parseLong(dataMask[1])));
+					mb.setMasked(MatchField.MPLS_LABEL, U32.of(ParseUtils.parseHexOrDecLong(dataMask[0])), 
+							U32.of(ParseUtils.parseHexOrDecLong(dataMask[1])));
 				}
 				break;
 			case STR_MPLS_TC:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.MPLS_TC, dataMask[0].contains("0x") ? U8.of(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.parseShort(dataMask[0])));
+					mb.setExact(MatchField.MPLS_TC, U8.of(ParseUtils.parseHexOrDecShort(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.MPLS_TC, dataMask[0].contains("0x") ? U8.of(Short.parseShort(dataMask[0].replaceFirst("0x", ""), 16)) : U8.of(Short.parseShort(dataMask[0])), 
-							dataMask[1].contains("0x") ? U8.of(Short.parseShort(dataMask[1].replaceFirst("0x", ""), 16)) : U8.of(Short.parseShort(dataMask[1])));
+					mb.setMasked(MatchField.MPLS_TC, U8.of(ParseUtils.parseHexOrDecShort(dataMask[0])), 
+							U8.of(ParseUtils.parseHexOrDecShort(dataMask[1])));
 				}
 				break;
 			case STR_MPLS_BOS:
-				mb.setExact(MatchField.MPLS_BOS, key_value[1].equalsIgnoreCase("true") ? OFBooleanValue.TRUE : OFBooleanValue.FALSE);
+				mb.setExact(MatchField.MPLS_BOS, OFBooleanValue.of(Boolean.parseBoolean(key_value[1])));
 				break;
 			case STR_METADATA:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.METADATA, dataMask[0].contains("0x") ? OFMetadata.ofRaw(Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16)) : OFMetadata.ofRaw(Long.parseLong(dataMask[0])));
+					mb.setExact(MatchField.METADATA, OFMetadata.ofRaw(ParseUtils.parseHexOrDecLong(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.METADATA, dataMask[0].contains("0x") ? OFMetadata.ofRaw(Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16)) : OFMetadata.ofRaw(Long.parseLong(dataMask[0])), 
-							dataMask[1].contains("0x") ? OFMetadata.ofRaw(Long.parseLong(dataMask[1].replaceFirst("0x", ""), 16)) : OFMetadata.ofRaw(Long.parseLong(dataMask[1])));
+					mb.setMasked(MatchField.METADATA, OFMetadata.ofRaw(ParseUtils.parseHexOrDecLong(dataMask[0])), 
+							OFMetadata.ofRaw(ParseUtils.parseHexOrDecLong(dataMask[1])));
 				}
 				break;
 			case STR_TUNNEL_ID:
 				if (dataMask.length == 1) {
-					mb.setExact(MatchField.TUNNEL_ID, dataMask[0].contains("0x") ? U64.of(Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16)) : U64.of(Long.parseLong(dataMask[0])));
+					mb.setExact(MatchField.TUNNEL_ID, U64.of(ParseUtils.parseHexOrDecLong(dataMask[0])));
 				} else {
-					mb.setMasked(MatchField.TUNNEL_ID, dataMask[0].contains("0x") ? U64.of(Long.parseLong(dataMask[0].replaceFirst("0x", ""), 16)) : U64.of(Long.parseLong(dataMask[0])), 
-							dataMask[1].contains("0x") ? U64.of(Long.parseLong(dataMask[1].replaceFirst("0x", ""), 16)) : U64.of(Long.parseLong(dataMask[1])));
+					mb.setMasked(MatchField.TUNNEL_ID, U64.of(ParseUtils.parseHexOrDecLong(dataMask[0])), 
+							U64.of(ParseUtils.parseHexOrDecLong(dataMask[1])));
 				}
 				break;
 			case STR_TUNNEL_IPV4_SRC:
@@ -807,17 +996,122 @@ public class MatchUtils {
 				}
 				break;
 			case STR_PBB_ISID:
-				/*TODO no-op. Not implemented.
+				log.warn("Ignoring unimplemented OXM {}", key_value[0]);
+				/*TODO no-op. Not implemented. 
 				if (key_value[1].startsWith("0x")) {
-					mb.setExact(MatchField., U64.of(Long.parseLong(key_value[1].replaceFirst("0x", ""), 16)));
+					mb.setExact(MatchField.pb, U64.of(Long.parseLong(key_value[1].replaceFirst("0x", ""), 16)));
 				} else {
 					mb.setExact(MatchField., U64.of(Long.parseLong(key_value[1])));
 				} */
 				break;
+			case STR_PBB_UCA:
+				mb.setExact(MatchField.PBB_UCA, OFBooleanValue.of(Boolean.parseBoolean(key_value[1])));
+				break;
+			case STR_TCP_FLAGS:
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.TCP_FLAGS, U16.of(ParseUtils.parseHexOrDecInt(dataMask[0])));
+				} else {
+					mb.setMasked(MatchField.TCP_FLAGS, U16.of(ParseUtils.parseHexOrDecInt(dataMask[0])), 
+							U16.of(ParseUtils.parseHexOrDecInt(dataMask[1])));
+				}
+				break;
+			case STR_ACTSET_OUTPUT: 
+				/* TODO when loxi bug fixed if (!mb.supports(MatchField.ACTSET_OUTPUT)) {
+					log.warn("Match {} unsupported in OpenFlow version {}", MatchField.ACTSET_OUTPUT, ofVersion);
+					break;
+				} else {
+					log.warn("Why are we here?");
+				}*/
+				if (dataMask.length == 1) {
+					mb.setExact(MatchField.ACTSET_OUTPUT, portFromString(dataMask[0]));
+				} else {
+					mb.setMasked(MatchField.ACTSET_OUTPUT, portFromString(dataMask[0]), portFromString(dataMask[1]));
+				}
+				break;
+			case STR_PACKET_TYPE:
+				if (dataMask.length != 2) {
+					log.error("Ignoring invalid PACKET_TYPE OXM. Must specify namespace and namespace type in the form 'ns/nstype'");
+				} else {
+					mb.setExact(MatchField.PACKET_TYPE, PacketType.of(ParseUtils.parseHexOrDecInt(dataMask[0]), 
+					        ParseUtils.parseHexOrDecInt(dataMask[1])));
+				}
+				break;
 			default:
 				throw new IllegalArgumentException("unknown token " + key_value + " parsing " + match);
 			} 
 		}
 		return mb.build();
 	}
-}
+		
+	public static OFPort portFromString(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("Port string cannot be null");
+        }
+        
+        s = s.trim().toLowerCase();
+        switch (s) {
+        case MatchUtils.STR_PORT_ALL:
+            return OFPort.ALL;
+        case MatchUtils.STR_PORT_CONTROLLER:
+            return OFPort.CONTROLLER;
+        case MatchUtils.STR_PORT_FLOOD:
+            return OFPort.FLOOD;
+        case MatchUtils.STR_PORT_IN_PORT:
+            return OFPort.IN_PORT;
+        case MatchUtils.STR_PORT_LOCAL:
+            return OFPort.LOCAL;
+        case MatchUtils.STR_PORT_NORMAL:
+            return OFPort.NORMAL;
+        case MatchUtils.STR_PORT_TABLE:
+            return OFPort.TABLE;
+        case MatchUtils.STR_PORT_MAX:
+            return OFPort.MAX;
+        case MatchUtils.STR_PORT_ANY:
+            return OFPort.ANY;
+        default:
+            log.debug("Port {} was not a special port string. Parsing as raw int or hex", s);
+        }
+
+        try {
+            return OFPort.of(U32.of(ParseUtils.parseHexOrDecLong(s)).getRaw());
+        } catch (NumberFormatException e) {
+            log.error("Could not parse port '{}'", s);
+            return null;
+        }
+    }
+	
+	public static String portToString(OFPort p) {
+        if (p == null) {
+            throw new IllegalArgumentException("Port cannot be null");
+        }
+        
+        if (p.equals(OFPort.ALL)) {
+            return STR_PORT_ALL;
+        } 
+        if (p.equals(OFPort.ANY)) {
+            return STR_PORT_ANY;
+        }
+        if (p.equals(OFPort.CONTROLLER)) {
+            return STR_PORT_CONTROLLER;
+        }
+        if (p.equals(OFPort.FLOOD)) {
+            return STR_PORT_FLOOD;
+        }
+        if (p.equals(OFPort.IN_PORT)) {
+            return STR_PORT_IN_PORT;
+        }
+        if (p.equals(OFPort.LOCAL)) {
+            return STR_PORT_LOCAL;
+        }
+        if (p.equals(OFPort.NORMAL)) {
+            return STR_PORT_NORMAL;
+        }
+        if (p.equals(OFPort.MAX)) {
+            return STR_PORT_MAX;
+        }
+        if (p.equals(OFPort.TABLE)) {
+            return STR_PORT_TABLE;
+        }
+        return Integer.toString(p.getPortNumber());
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
index 3e1b86f59362d077e513705b76da1c235f956b7b..7e53a5ad0180208facaad7226ddb3ea65256783a 100644
--- a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
+++ b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
@@ -16,7 +16,7 @@
 
 package net.floodlightcontroller.util;
 
-import java.io.IOException;
+import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Set;
 
@@ -24,6 +24,8 @@ import net.floodlightcontroller.core.IOFSwitch;
 
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Dampens OFMessages sent to an OF switch. A message is only written to 
@@ -33,6 +35,7 @@ import org.projectfloodlight.openflow.protocol.OFType;
  *
  */
 public class OFMessageDamper {
+    private static final Logger log = LoggerFactory.getLogger(OFMessageDamper.class);
     /**
      * An entry in the TimedCache. A cache entry consists of the sent message
      * as well as the switch to which the message was sent. 
@@ -42,7 +45,7 @@ public class OFMessageDamper {
      * obviously be more time-consuming.... 
      * 
      * We also store a reference to the actual IOFSwitch object and /not/
-     * the switch DPID. This way we are guarnteed to not dampen messages if
+     * the switch DPID. This way we are guaranteed to not dampen messages if
      * a switch disconnects and then reconnects.
      * 
      * @author gregor
@@ -62,7 +65,7 @@ public class OFMessageDamper {
         public int hashCode() {
             final int prime = 31;
             int result = 1;
-            result = prime * result + ((msg == null) ? 0 : msg.hashCode());
+            result = prime * result + ((msg == null) ? 0 : msg.hashCodeIgnoreXid());
             result = prime * result + ((sw == null) ? 0 : sw.hashCode());
             return result;
         }
@@ -77,7 +80,7 @@ public class OFMessageDamper {
             DamperEntry other = (DamperEntry) obj;
             if (msg == null) {
                 if (other.msg != null) return false;
-            } else if (!msg.equals(other.msg)) return false;
+            } else if (!msg.equalsIgnoreXid(other.msg)) return false;
             if (sw == null) {
                 if (other.sw != null) return false;
             } else if (!sw.equals(other.sw)) return false;
@@ -86,8 +89,10 @@ public class OFMessageDamper {
         
       
     }
+    
     TimedCache<DamperEntry> cache;
     EnumSet<OFType> msgTypesToCache;
+    
     /**
      * 
      * @param capacity the maximum number of messages that should be 
@@ -111,10 +116,10 @@ public class OFMessageDamper {
      * @param msg
      * @return true if the message was written to the switch, false if
      * the message was dampened. 
-     * @throws IOException
      */
-    public boolean write(IOFSwitch sw, OFMessage msg) throws IOException {
+    public boolean write(IOFSwitch sw, OFMessage msg) {
         if (!msgTypesToCache.contains(msg.getType())) {
+            log.debug("Not dampening this type of msg {}", msg);
             sw.write(msg);
             return true;
         }
@@ -122,10 +127,28 @@ public class OFMessageDamper {
         DamperEntry entry = new DamperEntry(msg, sw);
         if (cache.update(entry)) {
             // entry exists in cache. Dampening.
+            log.debug("Dampening cached msg {}", msg);
             return false; 
         } else {
+            log.debug("Not dampening new msg {}", msg);
             sw.write(msg);
             return true;
         }
     }
-}
+    
+    /**
+     * Wrapper around {@link OFMessageDamper#write(IOFSwitch, OFMessage)}. 
+     * @param sw
+     * @param msgs
+     * @return false if *any* message was dampened; true if no messages were dampened
+     */
+    public boolean write(IOFSwitch sw, Collection<OFMessage> msgs) {
+        boolean allWritten = true;
+        for (OFMessage msg : msgs) {
+            if (!write(sw, msg)) {
+                allWritten = false;
+            }
+        }
+        return allWritten;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java b/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java
index 51e2668be0a8338a39289979e5ad96009a2b5796..ffca61530dbcab3eda7159bec0a7f86014682ac1 100644
--- a/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java
@@ -6,6 +6,7 @@ import java.util.List;
 
 import net.floodlightcontroller.core.IOFSwitch;
 
+import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
 import org.projectfloodlight.openflow.protocol.OFPacketOut;
@@ -33,7 +34,7 @@ public class OFMessageUtils {
 	 * Prevent instantiation
 	 */
 	private OFMessageUtils() {};
-	
+
 	/**
 	 * Get the ingress port of a packet-in message. The manner in which
 	 * this is done depends on the OpenFlow version. OF1.0 and 1.1 have
@@ -46,7 +47,31 @@ public class OFMessageUtils {
 	public static OFPort getInPort(OFPacketIn pi) {
 		return pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT);
 	}
-	
+
+	/**
+	 * Set the ingress port of a packet-out message. The manner in which
+	 * this is done depends on the OpenFlow version. OF1.0 thru 1.4 have
+	 * a specific in_port field, while OF1.5+ store this information in
+	 * the packet-out's match field.
+	 * 
+	 * @param pob, the OFPacketOut.Builder within which to set the in port
+	 * @param in, the ingress OFPort
+	 */
+	public static void setInPort(OFPacketOut.Builder pob, OFPort in) {
+		if (pob.getVersion().compareTo(OFVersion.OF_15) < 0) { 
+			pob.setInPort(in);
+		} else if (pob.getMatch() != null) {
+			pob.getMatch().createBuilder()
+			.setExact(MatchField.IN_PORT, in)
+			.build();
+		} else {
+			pob.setMatch(OFFactories.getFactory(pob.getVersion())
+					.buildMatch()
+					.setExact(MatchField.IN_PORT, in)
+					.build());
+		}
+	}
+
 	/**
 	 * Get the VLAN on which this packet-in message was received.
 	 * @param pi, the OFPacketIn
@@ -55,7 +80,7 @@ public class OFMessageUtils {
 	public static OFVlanVidMatch getVlan(OFPacketIn pi) {
 		return pi.getMatch().get(MatchField.VLAN_VID) == null ? OFVlanVidMatch.UNTAGGED : pi.getMatch().get(MatchField.VLAN_VID);
 	}
-	
+
 	/**
 	 * Returns true if each object is deeply-equal in the same manner that
 	 * Object's equals() does with the exception of the XID field, which is
@@ -73,7 +98,7 @@ public class OFMessageUtils {
 		OFMessage.Builder mb = b.createBuilder().setXid(a.getXid());
 		return a.equals(mb.build());
 	}
-	
+
 	/**
 	 * Writes an OFPacketOut message to a switch.
 	 * 
@@ -86,14 +111,14 @@ public class OFMessageUtils {
 	 */
 	public static void writePacketOutForPacketIn(IOFSwitch sw,
 			OFPacketIn packetInMessage, OFPort egressPort) {
-		
+
 		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
 
 		// Set buffer_id, in_port, actions_len
 		pob.setBufferId(packetInMessage.getBufferId());
 		pob.setInPort(packetInMessage.getVersion().compareTo(OFVersion.OF_12) < 0 ? packetInMessage
 				.getInPort() : packetInMessage.getMatch().get(
-				MatchField.IN_PORT));
+						MatchField.IN_PORT));
 
 		// set actions
 		List<OFAction> actions = new ArrayList<OFAction>(1);
diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java b/src/main/java/net/floodlightcontroller/util/OXMUtils.java
similarity index 78%
rename from src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java
rename to src/main/java/net/floodlightcontroller/util/OXMUtils.java
index c0b13916edfe037ab43e298f68be93fe18279967..bf757b99135ee9932b81b1137ec5837ba3978d2c 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java
+++ b/src/main/java/net/floodlightcontroller/util/OXMUtils.java
@@ -1,8 +1,10 @@
-package net.floodlightcontroller.core.web.serializers;
+package net.floodlightcontroller.util;
 
-import net.floodlightcontroller.util.MatchUtils;
-
-import org.projectfloodlight.openflow.protocol.ver14.OFOxmClassSerializerVer14;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.protocol.ver15.OFOxmClassSerializerVer15;
 import org.projectfloodlight.openflow.types.ArpOpcode;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.ICMPv4Code;
@@ -17,7 +19,10 @@ import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBooleanValue;
 import org.projectfloodlight.openflow.types.OFMetadata;
 import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.PacketType;
 import org.projectfloodlight.openflow.types.TransportPort;
+import org.projectfloodlight.openflow.types.U16;
 import org.projectfloodlight.openflow.types.U32;
 import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.U8;
@@ -57,21 +62,19 @@ import com.google.common.collect.HashBiMap;
  * @author Ryan Izard, rizard@g.clemson.edu, ryan.izard@bigswitch.com
  *
  */
-public class OXMSerializer {	
+public class OXMUtils {	
 	
 	/*
 	 * OXM IDs for OpenFlow versions 1.5 and below are the same, 
 	 * where each version simply adds to the already populated
 	 * list of OXM IDs. (IDs don't change b/t OF versions.)
-	 * As such, we'll assume OF1.4 here, which includes 1.3.
-	 * 
-	 * TODO Later, when Loxi supports 1.5, we'll change to
-	 * OFOxmClassSerializerVer15.
+	 * As such, we'll assume OF1.5 here, which includes 1.3.
 	 */
 	
 	/* String constants */
 	private static final String STR_MASKED = "_masked";
 	private static final String STR_NXM = "nxm_";
+	private static final String STR_PKT_REG = "pkt_reg_";
 	
 	/* Bit shifting constants */
 	private static final int SHIFT_FIELD = 9;
@@ -82,10 +85,10 @@ public class OXMSerializer {
 	private static final int MASKED = (1 << SHIFT_HAS_MASK);
 	
 	/* Classes */
-	private static final int OF_BASIC = (OFOxmClassSerializerVer14.OPENFLOW_BASIC_VAL << SHIFT_CLASS) & 0xFFffFFff;
-	//TODO private static final int OF_PKT_REG = (0x8001 << SHIFT_CLASS) & 0xFFffFFff;
-	private static final int NXM_0 = (OFOxmClassSerializerVer14.NXM_0_VAL << SHIFT_CLASS) & 0xFFffFFff;
-	private static final int NXM_1 = (OFOxmClassSerializerVer14.NXM_1_VAL << SHIFT_CLASS) & 0xFFffFFff;
+	private static final int OF_BASIC = (OFOxmClassSerializerVer15.OPENFLOW_BASIC_VAL << SHIFT_CLASS) & 0xFFffFFff;
+	private static final int OF_PKT_REG = (OFOxmClassSerializerVer15.PACKET_REGS_VAL << SHIFT_CLASS) & 0xFFffFFff;
+	private static final int NXM_0 = (OFOxmClassSerializerVer15.NXM_0_VAL << SHIFT_CLASS) & 0xFFffFFff;
+	private static final int NXM_1 = (OFOxmClassSerializerVer15.NXM_1_VAL << SHIFT_CLASS) & 0xFFffFFff;
 	
 	/* The bi-directional map we'll use to make this process more efficient */
 	private static final BiMap<U32, String> map = HashBiMap.create();
@@ -187,7 +190,6 @@ public class OXMSerializer {
 		map.put(U32.ofRaw(OF_BASIC | (26 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_SRC);
 		map.put(U32.ofRaw(OF_BASIC | (26 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_SRC + STR_MASKED);
 		
-		
 		map.put(U32.ofRaw(OF_BASIC | (27 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_DST);
 		map.put(U32.ofRaw(OF_BASIC | (27 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_DST + STR_MASKED);
 		
@@ -203,11 +205,11 @@ public class OXMSerializer {
 		map.put(U32.ofRaw(OF_BASIC | (31 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_ND_TARGET);
 		map.put(U32.ofRaw(OF_BASIC | (31 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_TARGET + STR_MASKED);
 		
-		map.put(U32.ofRaw(OF_BASIC | (32 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_IPV6_ND_SSL);
-		map.put(U32.ofRaw(OF_BASIC | (32 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_SSL + STR_MASKED);
+		map.put(U32.ofRaw(OF_BASIC | (32 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_IPV6_ND_SLL);
+		map.put(U32.ofRaw(OF_BASIC | (32 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_SLL + STR_MASKED);
 		
-		map.put(U32.ofRaw(OF_BASIC | (33 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_IPV6_ND_TTL);
-		map.put(U32.ofRaw(OF_BASIC | (33 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_TTL + STR_MASKED);
+		map.put(U32.ofRaw(OF_BASIC | (33 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_IPV6_ND_TLL);
+		map.put(U32.ofRaw(OF_BASIC | (33 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_TLL + STR_MASKED);
 		
 		map.put(U32.ofRaw(OF_BASIC | (34 << SHIFT_FIELD) | U32.ZERO.getLength()), MatchUtils.STR_MPLS_LABEL);
 		map.put(U32.ofRaw(OF_BASIC | (34 << SHIFT_FIELD) | U32.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_LABEL + STR_MASKED);
@@ -218,31 +220,28 @@ public class OXMSerializer {
 		map.put(U32.ofRaw(OF_BASIC | (36 << SHIFT_FIELD) | OFBooleanValue.TRUE.getLength()), MatchUtils.STR_MPLS_BOS);
 		map.put(U32.ofRaw(OF_BASIC | (36 << SHIFT_FIELD) | OFBooleanValue.TRUE.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_BOS + STR_MASKED);
 		
-		/*
-		 * TODO in Loxi and MatchUtils
-		 * map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | ), MatchUtils.STR_PBB_ISID);
-		 */
-		map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | 3 ), MatchUtils.STR_PBB_ISID); /* need to update after Loxi support */
+		map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | 3 ), MatchUtils.STR_PBB_ISID);
 		map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | 3 * 2 | MASKED ), MatchUtils.STR_PBB_ISID + STR_MASKED);
 		
 		map.put(U32.ofRaw(OF_BASIC | (38 << SHIFT_FIELD) | U64.ZERO.getLength()), MatchUtils.STR_TUNNEL_ID);
 		map.put(U32.ofRaw(OF_BASIC | (38 << SHIFT_FIELD) | U64.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_TUNNEL_ID + STR_MASKED);
 		
-		/* 
-		 * TODO in MatchUtils
-		 * map.put(U32.ofRaw(OF_BASIC | 39), MatchUtils.STR_IPV6_EXTHDR);
-		 */
-		map.put(U32.ofRaw(OF_BASIC | (39 << SHIFT_FIELD) | 2), "ipv6_exthdr"); /* need to update after Loxi support */
-		map.put(U32.ofRaw(OF_BASIC | (39 << SHIFT_FIELD) | 2 * 2 | MASKED), "ipv6_exthdr" + STR_MASKED);
+		map.put(U32.ofRaw(OF_BASIC | (39 << SHIFT_FIELD) | 2), MatchUtils.STR_IPV6_EXTHDR);
+		map.put(U32.ofRaw(OF_BASIC | (39 << SHIFT_FIELD) | 2 * 2 | MASKED), MatchUtils.STR_IPV6_EXTHDR + STR_MASKED);
 		
 		/* note skip of ID 40 here according to spec */
 		
-		/* 
-		 * TODO in MatchUtils
-		 * map.put(U32.ofRaw(OF_BASIC | 41), MatchUtils.STR_PBB_UCA);
-		 */
-		map.put(U32.ofRaw(OF_BASIC | (41 << SHIFT_FIELD) | 1), "pbb_uca");
-		map.put(U32.ofRaw(OF_BASIC | (41 << SHIFT_FIELD) | 1 * 2 | MASKED), "pbb_uca" + STR_MASKED);
+		map.put(U32.ofRaw(OF_BASIC | (41 << SHIFT_FIELD) | 1), MatchUtils.STR_PBB_UCA);
+		map.put(U32.ofRaw(OF_BASIC | (41 << SHIFT_FIELD) | 1 * 2 | MASKED), MatchUtils.STR_PBB_UCA + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (42 << SHIFT_FIELD) | U32.ZERO.getLength()), MatchUtils.STR_TCP_FLAGS);
+		map.put(U32.ofRaw(OF_BASIC | (42 << SHIFT_FIELD) | U32.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_TCP_FLAGS + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (43 << SHIFT_FIELD) | 1), MatchUtils.STR_ACTSET_OUTPUT);
+		map.put(U32.ofRaw(OF_BASIC | (43 << SHIFT_FIELD) | 1 * 2 | MASKED), MatchUtils.STR_ACTSET_OUTPUT + STR_MASKED);
+		
+		map.put(U32.ofRaw(OF_BASIC | (44 << SHIFT_FIELD) | U32.ZERO.getLength()), MatchUtils.STR_PACKET_TYPE);
+		map.put(U32.ofRaw(OF_BASIC | (44 << SHIFT_FIELD) | U32.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_PACKET_TYPE + STR_MASKED);
 		
 		/*
 		 * NXM_0 Definitions
@@ -352,11 +351,11 @@ public class OXMSerializer {
 		map.put(U32.ofRaw(NXM_1 | (23 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_TARGET);
 		map.put(U32.ofRaw(NXM_1 | (23 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_TARGET + STR_MASKED);
 		
-		map.put(U32.ofRaw(NXM_1 | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_SSL);
-		map.put(U32.ofRaw(NXM_1 | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_SSL + STR_MASKED);
+		map.put(U32.ofRaw(NXM_1 | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_SLL);
+		map.put(U32.ofRaw(NXM_1 | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_SLL + STR_MASKED);
 		
-		map.put(U32.ofRaw(NXM_1 | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_TTL);
-		map.put(U32.ofRaw(NXM_1 | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_TTL + STR_MASKED);
+		map.put(U32.ofRaw(NXM_1 | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_TLL);
+		map.put(U32.ofRaw(NXM_1 | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_TLL + STR_MASKED);
 		
 		map.put(U32.ofRaw(NXM_1 | (26 << SHIFT_FIELD) | 1), STR_NXM + "ip_frag");
 		map.put(U32.ofRaw(NXM_1 | (26 << SHIFT_FIELD) | 1 * 2 | MASKED), STR_NXM + "ip_frag" + STR_MASKED);
@@ -394,16 +393,22 @@ public class OXMSerializer {
 		map.put(U32.ofRaw(NXM_1 | (37 << SHIFT_FIELD) | 4), STR_NXM + MatchUtils.STR_PBB_ISID);
 		map.put(U32.ofRaw(NXM_1 | (37 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + MatchUtils.STR_PBB_ISID + STR_MASKED);
 		
+		/* "Register" max number of packet registers */
+		for (int i = 0; i < 128; i++) {
+			map.put(U32.ofRaw(OF_PKT_REG | (i << SHIFT_FIELD) | 8), STR_PKT_REG + Integer.toString(i));
+			map.put(U32.ofRaw(OF_PKT_REG | (i << SHIFT_FIELD) | 8 * 2 | MASKED), STR_PKT_REG + Integer.toString(i) + STR_MASKED);
+		}
 	}
 	
 	/**
-	 * Converts the string representation of an OXM ID as
-	 * defined in {@link net.floodlightcontroller.util.MatchUtils.java}
-	 * to its corresponding U32 OXM ID.
+	 * Converts the U32 of an OXM ID to its string representation as
+	 * defined in {@link net.floodlightcontroller.util.MatchUtils.java}.
 	 * 
 	 * Any NXM (not defined in {@link net.floodlightcontroller.util.MatchUtils.java}
 	 * is the same as the OXM string with an "nxm_" prefix.
 	 * 
+	 * Any packet register is preceded by a "pkt_reg_" prefix.
+	 * 
 	 * Any OXM or NXM that is masked include a "_masked" postfix.
 	 * 
 	 * @param oxmId, the U32 defining the OXM/NXM as [class]+[field]+[has_mask]+[length]
@@ -425,9 +430,11 @@ public class OXMSerializer {
 	 * Any NXM (not defined in {@link net.floodlightcontroller.util.MatchUtils.java}
 	 * is the same as the OXM string with an "nxm_" prefix.
 	 * 
+	 * Any packet register is preceded by a "pkt_reg_" prefix.
+	 * 
 	 * Any OXM or NXM that is masked include a "_masked" postfix.
 	 * 
-	 * @param oxmString, the string definging the OXM/NXM
+	 * @param oxmString, the string defining the OXM/NXM
 	 * @return the U32 representing the OXM/NXM as [class]+[field]+[has_mask]+[length]
 	 */
 	public static U32 oxmStringToId(String oxmString) {
@@ -437,4 +444,131 @@ public class OXMSerializer {
 			return null;
 		}
 	}
+	
+	/**
+	 * Get an unset/default OXM object.
+	 * 
+	 * @param oxmString
+	 * @param v
+	 * @return
+	 */
+	public static OFOxm<?> oxmStringToOxm(String oxmString, OFVersion v) {
+		OFFactory f = OFFactories.getFactory(v);
+		switch (oxmString) {
+		case MatchUtils.STR_ACTSET_OUTPUT:
+			return f.oxms().actsetOutput(OFPort.ZERO);
+			
+		case MatchUtils.STR_ARP_DHA:
+			return f.oxms().arpTha(MacAddress.NONE);
+		case MatchUtils.STR_ARP_DPA:
+			return f.oxms().arpTpa(IPv4Address.NONE);
+		case MatchUtils.STR_ARP_OPCODE:
+			return f.oxms().arpOp(ArpOpcode.NONE);
+		case MatchUtils.STR_ARP_SHA:
+			return f.oxms().arpSha(MacAddress.NONE);
+		case MatchUtils.STR_ARP_SPA:
+			return f.oxms().arpSpa(IPv4Address.NONE);
+			
+		case MatchUtils.STR_DL_DST:
+			return f.oxms().ethDst(MacAddress.NONE);
+		case MatchUtils.STR_DL_SRC:
+			return f.oxms().ethSrc(MacAddress.NONE);
+		case MatchUtils.STR_DL_TYPE:
+			return f.oxms().ethType(EthType.NONE);
+		case MatchUtils.STR_DL_VLAN:
+			return f.oxms().vlanVid(OFVlanVidMatch.NONE);
+		case MatchUtils.STR_DL_VLAN_PCP:
+			return f.oxms().vlanPcp(VlanPcp.NONE);
+			
+		case MatchUtils.STR_ICMP_CODE:
+			return f.oxms().icmpv4Code(ICMPv4Code.NONE);
+		case MatchUtils.STR_ICMP_TYPE:
+			return f.oxms().icmpv4Type(ICMPv4Type.NONE);
+		case MatchUtils.STR_ICMPV6_CODE:
+			return f.oxms().icmpv6Code(U8.ZERO);
+		case MatchUtils.STR_ICMPV6_TYPE:
+			return f.oxms().icmpv6Type(U8.ZERO);
+			
+		case MatchUtils.STR_IN_PHYS_PORT:
+			return f.oxms().inPhyPort(OFPort.ZERO);
+		case MatchUtils.STR_IN_PORT:
+			return f.oxms().inPort(OFPort.ZERO);
+
+		case MatchUtils.STR_IPV6_DST:
+			return f.oxms().ipv6Dst(IPv6Address.NONE);
+		case MatchUtils.STR_IPV6_EXTHDR:
+			return f.oxms().ipv6Exthdr(U16.ZERO);
+		case MatchUtils.STR_IPV6_FLOW_LABEL:
+			return f.oxms().ipv6Flabel(IPv6FlowLabel.NONE);
+		case MatchUtils.STR_IPV6_ND_SLL:
+			return f.oxms().ipv6NdSll(MacAddress.NONE);
+		case MatchUtils.STR_IPV6_ND_TARGET:
+			return f.oxms().ipv6NdTarget(IPv6Address.NONE);
+		case MatchUtils.STR_IPV6_ND_TLL:
+			return f.oxms().ipv6NdTll(MacAddress.NONE);
+		case MatchUtils.STR_IPV6_SRC:
+			return f.oxms().ipv6Src(IPv6Address.NONE);
+			
+		case MatchUtils.STR_METADATA:
+			return f.oxms().metadata(OFMetadata.NONE);
+			
+		case MatchUtils.STR_MPLS_BOS:
+			return f.oxms().mplsBos(OFBooleanValue.FALSE);
+		case MatchUtils.STR_MPLS_LABEL:
+			return f.oxms().mplsLabel(U32.ZERO);
+		case MatchUtils.STR_MPLS_TC:
+			return f.oxms().mplsTc(U8.ZERO);
+			
+		case MatchUtils.STR_NW_DSCP:
+			return f.oxms().ipDscp(IpDscp.NONE);
+		case MatchUtils.STR_NW_DST:
+			return f.oxms().ipv4Dst(IPv4Address.NONE);
+		case MatchUtils.STR_NW_ECN:
+			return f.oxms().ipEcn(IpEcn.NONE);
+		case MatchUtils.STR_NW_PROTO:
+			return f.oxms().ipProto(IpProtocol.NONE);
+		case MatchUtils.STR_NW_SRC:
+			return f.oxms().ipv4Src(IPv4Address.NONE);
+		case MatchUtils.STR_NW_TOS:
+			return null; /* should use ECN or DSCP */
+			
+		case MatchUtils.STR_PACKET_TYPE:
+			return f.oxms().packetType(PacketType.NO_PACKET);
+			
+		case MatchUtils.STR_PBB_ISID:
+			return null; /* TODO not implemented */
+		case MatchUtils.STR_PBB_UCA:
+			return f.oxms().pbbUca(OFBooleanValue.FALSE);
+			
+		case MatchUtils.STR_SCTP_DST:
+			return f.oxms().sctpDst(TransportPort.NONE);
+		case MatchUtils.STR_SCTP_SRC:
+			return f.oxms().sctpSrc(TransportPort.NONE);
+		case MatchUtils.STR_TCP_DST:
+			return f.oxms().tcpDst(TransportPort.NONE);
+		case MatchUtils.STR_TCP_FLAGS:
+			return f.oxms().tcpFlags(U16.ZERO);
+		case MatchUtils.STR_TCP_SRC:
+			return f.oxms().tcpSrc(TransportPort.NONE);
+			
+		case MatchUtils.STR_TP_DST:
+		case MatchUtils.STR_TP_SRC:
+			return null; /* should use specific transport protocol */
+			
+		case MatchUtils.STR_TUNNEL_ID:
+			return f.oxms().tunnelId(U64.ZERO);
+		case MatchUtils.STR_TUNNEL_IPV4_DST:
+			return f.oxms().tunnelIpv4Dst(IPv4Address.NONE);
+		case MatchUtils.STR_TUNNEL_IPV4_SRC:
+			return f.oxms().tunnelIpv4Src(IPv4Address.NONE);
+			
+		case MatchUtils.STR_UDP_DST:
+			return f.oxms().udpDst(TransportPort.NONE);
+		case MatchUtils.STR_UDP_SRC:
+			return f.oxms().udpSrc(TransportPort.NONE);
+
+		default:
+			return null; /* unimplemented */
+		}
+	}
 }
diff --git a/src/main/java/net/floodlightcontroller/util/ParseUtils.java b/src/main/java/net/floodlightcontroller/util/ParseUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..c09e181ddd0a84c29d60b942ac75396405447bf5
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/ParseUtils.java
@@ -0,0 +1,131 @@
+package net.floodlightcontroller.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple class to assist in parsing hex or decimal primitive types
+ * from String to their respective types.
+ * 
+ * Use this in place of e.g. Integer.decode() if you wish to avoid 
+ * unnecessarily creating objects when you want just the primitive type.
+ *  
+ * @author rizard
+ *
+ */
+public class ParseUtils {
+    private static final Logger log = LoggerFactory.getLogger(ParseUtils.class);
+    private static final byte exceptionReturnValue = 0;
+    
+    /**
+     * Parse an int from a String.
+     * 
+     * Hex is expected to have a leading "0x".
+     * 
+     * @throws NumberFormatException
+     * @param s
+     * @return
+     */
+    public static int parseHexOrDecInt(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+        s = s.trim().toLowerCase();
+        if (s.startsWith("0x")) {
+            return Integer.parseInt(s.replaceFirst("0x", ""), 16);
+        } else {
+            return Integer.parseInt(s);                   
+        }
+    }
+
+    /**
+     * Parse a short from a String.
+     * 
+     * Hex is expected to have a leading "0x".
+     * 
+     * @throws NumberFormatException
+     * @param s
+     * @return
+     */
+    public static short parseHexOrDecShort(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+        s = s.trim().toLowerCase();
+        try {
+            if (s.startsWith("0x")) {
+                return Short.parseShort(s.replaceFirst("0x", ""), 16);
+            } else {
+                return Short.parseShort(s);                   
+            }
+        } catch (NumberFormatException e) {
+            log.error("Could not parse short {}. Returning default", s);
+            return exceptionReturnValue;
+        }
+    }
+
+    /**
+     * Parse a long from a String.
+     * 
+     * Hex is expected to have a leading "0x".
+     * 
+     * @throws NumberFormatException
+     * @param s
+     * @return
+     */
+    public static long parseHexOrDecLong(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+        s = s.trim().toLowerCase();
+        if (s.startsWith("0x")) {
+            return Long.parseLong(s.replaceFirst("0x", ""), 16);
+        } else {
+            return Long.parseLong(s);                   
+        }
+    }
+
+    /**
+     * Parse a byte from a String.
+     * 
+     * Hex is expected to have a leading "0x".
+     * 
+     * @throws NumberFormatException
+     * @param s
+     * @return
+     */
+    public static byte parseHexOrDecByte(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+        s = s.trim().toLowerCase();
+        if (s.startsWith("0x")) {
+            return Byte.parseByte(s.replaceFirst("0x", ""), 16);
+        } else {
+            return Byte.parseByte(s);                   
+        }
+    }
+
+    /**
+     * Convert any number to a boolean, where any positive number is
+     * true and zero or negative number is false. The largest supported
+     * type is a Long.
+     * 
+     * Hex is expected to have a leading "0x".
+     * 
+     * @throws NumberFormatException
+     * @param s
+     * @return true or false
+     */
+    public static boolean parseHexOrDecBool(String s) {
+        if (s == null) {
+            throw new IllegalArgumentException("String cannot be null");
+        }
+        s = s.trim().toLowerCase();
+        if (s.startsWith("0x")) {
+            return Long.parseLong(s.replaceFirst("0x", ""), 16) > 0;
+        } else {
+            return Long.parseLong(s) > 0;                   
+        }
+    }
+}
diff --git a/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncManager.java b/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncManager.java
index fbe81c5b81ecd970a60ca6817f36417b9205d5b7..595f378ff1753c63f1c7e081cfd58bba67be946a 100644
--- a/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncManager.java
+++ b/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncManager.java
@@ -3,7 +3,6 @@ package org.sdnplatform.sync.internal.remote;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Future;
@@ -29,7 +28,6 @@ import org.sdnplatform.sync.error.SyncRuntimeException;
 import org.sdnplatform.sync.error.UnknownStoreException;
 import org.sdnplatform.sync.internal.AbstractSyncManager;
 import org.sdnplatform.sync.internal.config.AuthScheme;
-import org.sdnplatform.sync.internal.config.ClusterConfig;
 import org.sdnplatform.sync.internal.rpc.IRPCListener;
 import org.sdnplatform.sync.internal.rpc.RPCService;
 import org.sdnplatform.sync.internal.rpc.TProtocolUtil;
diff --git a/src/main/java/org/sdnplatform/sync/internal/util/CryptoUtil.java b/src/main/java/org/sdnplatform/sync/internal/util/CryptoUtil.java
index 3cbece71242d654e85e0f6ad21b33ffd201b8e21..49d2b7d6ef6521bdb13caecd17a5dad4d2ee33ae 100644
--- a/src/main/java/org/sdnplatform/sync/internal/util/CryptoUtil.java
+++ b/src/main/java/org/sdnplatform/sync/internal/util/CryptoUtil.java
@@ -5,13 +5,9 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.security.Key;
 import java.security.KeyStore;
-import java.security.KeyStore.Entry;
 import java.security.SecureRandom;
-import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 
-import org.sdnplatform.sync.ISyncService;
-
 public class CryptoUtil {
     private static SecureRandom secureRandom = new SecureRandom();
 
diff --git a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
index 4aa8eb12eb21ecfd0cc84558d493e12930aec27c..fe0e65f518d97bde01ca6da5f7db5921aa8c7919 100644
--- a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
+++ b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
@@ -4,7 +4,6 @@ net.floodlightcontroller.flowcache.FlowReconcileManager
 net.floodlightcontroller.hub.Hub
 net.floodlightcontroller.jython.JythonDebugInterface
 net.floodlightcontroller.debugcounter.DebugCounterServiceImpl
-net.floodlightcontroller.debugevent.DebugEventService
 net.floodlightcontroller.threadpool.ThreadPool
 net.floodlightcontroller.perfmon.PktInProcessingTime
 net.floodlightcontroller.restserver.RestApiServer
@@ -17,8 +16,7 @@ net.floodlightcontroller.core.internal.ShutdownServiceImpl
 org.sdnplatform.sync.internal.SyncManager
 org.sdnplatform.sync.internal.SyncTorture
 net.floodlightcontroller.simpleft.FT
-net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher
-net.floodlightcontroller.testmodule.TestModule
+net.floodlightcontroller.staticentry.StaticEntryPusher
 net.floodlightcontroller.topology.TopologyManager
 net.floodlightcontroller.forwarding.Forwarding
 net.floodlightcontroller.loadbalancer.LoadBalancer
diff --git a/src/main/resources/floodlightNodeBackup.properties b/src/main/resources/floodlightNodeBackup.properties
index 52aaf0fe36375da8c14b3e5d8026b605b943305b..82d97aa68310d12700fa77db0759a958f8fd0d61 100644
--- a/src/main/resources/floodlightNodeBackup.properties
+++ b/src/main/resources/floodlightNodeBackup.properties
@@ -4,8 +4,7 @@ net.floodlightcontroller.core.internal.FloodlightProvider,\
 net.floodlightcontroller.threadpool.ThreadPool,\
 net.floodlightcontroller.debugcounter.DebugCounterServiceImpl,\
 net.floodlightcontroller.perfmon.PktInProcessingTime,\
-net.floodlightcontroller.debugevent.DebugEventService,\
-net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
+net.floodlightcontroller.staticentry.StaticEntryPusher,\
 net.floodlightcontroller.topology.TopologyManager,\
 net.floodlightcontroller.forwarding.Forwarding,\
 net.floodlightcontroller.linkdiscovery.internal.LinkDiscoveryManager,\
diff --git a/src/main/resources/floodlightbenchmarking.properties b/src/main/resources/floodlightbenchmarking.properties
new file mode 100644
index 0000000000000000000000000000000000000000..9a61646fbc50db870b25237f916ceccb48dff39e
--- /dev/null
+++ b/src/main/resources/floodlightbenchmarking.properties
@@ -0,0 +1,29 @@
+floodlight.modules=\
+net.floodlightcontroller.storage.memory.MemoryStorageSource,\
+net.floodlightcontroller.core.internal.FloodlightProvider,\
+net.floodlightcontroller.threadpool.ThreadPool,\
+net.floodlightcontroller.debugcounter.DebugCounterServiceImpl,\
+net.floodlightcontroller.perfmon.PktInProcessingTime,\
+net.floodlightcontroller.restserver.RestApiServer,\
+net.floodlightcontroller.hub.Hub
+net.floodlightcontroller.core.internal.FloodlightProvider.openFlowPort=6653
+net.floodlightcontroller.core.internal.FloodlightProvider.role=ACTIVE
+net.floodlightcontroller.core.internal.FloodlightProvider.workerThreads=8
+net.floodlightcontroller.core.internal.FloodlightProvider.controllerId=1
+net.floodlightcontroller.core.internal.OFSwitchManager.defaultMaxTablesToReceiveTableMissFlow=1
+net.floodlightcontroller.core.internal.OFSwitchManager.maxTablesToReceiveTableMissFlowPerDpid={"00:00:00:00:00:00:00:01":"1","2":"1"}
+net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnInitialHandshakeAsMaster=YES
+net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnEachTransitionToMaster=YES
+net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePath=/path/to.jecks
+net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePassword=PassFL
+net.floodlightcontroller.core.internal.OFSwitchManager.useSsl=NO
+net.floodlightcontroller.core.internal.OFSwitchManager.supportedOpenFlowVersions=1.0, 1.1, 1.2, 1.3, 1.4, 1.5
+net.floodlightcontroller.core.internal.OFSwitchManager.switchesInitialState={"00:00:00:00:00:00:00:01":"ROLE_MASTER","00:00:00:00:00:00:00:02":"ROLE_MASTER", "00:00:00:00:00:00:00:03":"ROLE_MASTER", "00:00:00:00:00:00:00:04":"ROLE_MASTER","00:00:00:00:00:00:00:05":"ROLE_MASTER"}
+net.floodlightcontroller.restserver.RestApiServer.keyStorePath=/path/to.jceks
+net.floodlightcontroller.restserver.RestApiServer.keyStorePassword=Password
+net.floodlightcontroller.restserver.RestApiServer.httpsNeedClientAuthentication=NO
+net.floodlightcontroller.restserver.RestApiServer.useHttps=NO
+net.floodlightcontroller.restserver.RestApiServer.useHttp=YES
+net.floodlightcontroller.restserver.RestApiServer.httpsPort=8081
+net.floodlightcontroller.restserver.RestApiServer.httpPort=8080
+net.floodlightcontroller.restserver.RestApiServer.accessControlAllowAllOrigins=TRUE
\ No newline at end of file
diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties
index b4fc22881c1c19c8bf3c382095be045b53116586..51ab0d18f243c5d874b91600d53ef44164357123 100644
--- a/src/main/resources/floodlightdefault.properties
+++ b/src/main/resources/floodlightdefault.properties
@@ -5,8 +5,7 @@ net.floodlightcontroller.core.internal.FloodlightProvider,\
 net.floodlightcontroller.threadpool.ThreadPool,\
 net.floodlightcontroller.debugcounter.DebugCounterServiceImpl,\
 net.floodlightcontroller.perfmon.PktInProcessingTime,\
-net.floodlightcontroller.debugevent.DebugEventService,\
-net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
+net.floodlightcontroller.staticentry.StaticEntryPusher,\
 net.floodlightcontroller.restserver.RestApiServer,\
 net.floodlightcontroller.topology.TopologyManager,\
 net.floodlightcontroller.forwarding.Forwarding,\
@@ -34,20 +33,24 @@ net.floodlightcontroller.forwarding.Forwarding.flood-arp=NO
 net.floodlightcontroller.forwarding.Forwarding.idle-timeout=5
 net.floodlightcontroller.forwarding.Forwarding.set-send-flow-rem-flag=FALSE
 net.floodlightcontroller.forwarding.Forwarding.remove-flows-on-link-or-port-down=TRUE
-net.floodlightcontroller.core.internal.FloodlightProvider.openFlowPort=6653
 net.floodlightcontroller.core.internal.FloodlightProvider.role=ACTIVE
-net.floodlightcontroller.core.internal.FloodlightProvider.workerThreads=8
 net.floodlightcontroller.core.internal.FloodlightProvider.controllerId=1
 net.floodlightcontroller.linkdiscovery.internal.LinkDiscoveryManager.latency-history-size=10
 net.floodlightcontroller.linkdiscovery.internal.LinkDiscoveryManager.latency-update-threshold=0.5
+net.floodlightcontroller.core.internal.OFSwitchManager.openFlowPort=6653
+net.floodlightcontroller.core.internal.OFSwitchManager.openFlowAddresses=0.0.0.0
+net.floodlightcontroller.core.internal.OFSwitchManager.workerThreads=16
+net.floodlightcontroller.core.internal.OFSwitchManager.bossThreads=1
+net.floodlightcontroller.core.internal.OFSwitchManager.connectionBacklog=1000
+net.floodlightcontroller.core.internal.OFSwitchManager.connectionTimeoutMs=60000
 net.floodlightcontroller.core.internal.OFSwitchManager.defaultMaxTablesToReceiveTableMissFlow=1
 net.floodlightcontroller.core.internal.OFSwitchManager.maxTablesToReceiveTableMissFlowPerDpid={"00:00:00:00:00:00:00:01":"1","2":"1"}
-net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnInitialHandshakeAsMaster=NO
-net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnEachTransitionToMaster=NO
+net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnInitialHandshakeAsMaster=YES
+net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnEachTransitionToMaster=YES
 net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePath=/path/to.jecks
 net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePassword=PassFL
 net.floodlightcontroller.core.internal.OFSwitchManager.useSsl=NO
-net.floodlightcontroller.core.internal.OFSwitchManager.supportedOpenFlowVersions=1.0, 1.1, 1.2, 1.3, 1.4
+net.floodlightcontroller.core.internal.OFSwitchManager.supportedOpenFlowVersions=1.0, 1.1, 1.2, 1.3, 1.4, 1.5
 net.floodlightcontroller.core.internal.OFSwitchManager.switchesInitialState={"00:00:00:00:00:00:00:01":"ROLE_MASTER","00:00:00:00:00:00:00:02":"ROLE_MASTER", "00:00:00:00:00:00:00:03":"ROLE_MASTER", "00:00:00:00:00:00:00:04":"ROLE_MASTER","00:00:00:00:00:00:00:05":"ROLE_MASTER"}
 net.floodlightcontroller.restserver.RestApiServer.keyStorePath=/path/to.jceks
 net.floodlightcontroller.restserver.RestApiServer.keyStorePassword=Password
@@ -56,6 +59,6 @@ net.floodlightcontroller.restserver.RestApiServer.useHttps=NO
 net.floodlightcontroller.restserver.RestApiServer.useHttp=YES
 net.floodlightcontroller.restserver.RestApiServer.httpsPort=8081
 net.floodlightcontroller.restserver.RestApiServer.httpPort=8080
-net.floodlightcontroller.restserver.RestApiServer.accessControlAllowAllOrigins=FALSE
+net.floodlightcontroller.restserver.RestApiServer.accessControlAllowAllOrigins=TRUE
 net.floodlightcontroller.statistics.StatisticsCollector.enable=FALSE
 net.floodlightcontroller.statistics.StatisticsCollector.collectionIntervalPortStatsSeconds=10
\ No newline at end of file
diff --git a/src/main/resources/learningswitch.properties b/src/main/resources/learningswitch.properties
index adaa4a51bc42cce981d2a1aa7568d925e9cc954e..e2f50a14a590b3bb3568d509f523772aeb06512e 100644
--- a/src/main/resources/learningswitch.properties
+++ b/src/main/resources/learningswitch.properties
@@ -5,8 +5,7 @@ net.floodlightcontroller.core.internal.FloodlightProvider,\
 net.floodlightcontroller.threadpool.ThreadPool,\
 net.floodlightcontroller.debugcounter.DebugCounterServiceImpl,\
 net.floodlightcontroller.perfmon.PktInProcessingTime,\
-net.floodlightcontroller.debugevent.DebugEventService,\
-net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
+net.floodlightcontroller.staticentry.StaticEntryPusher,\
 net.floodlightcontroller.restserver.RestApiServer,\
 net.floodlightcontroller.topology.TopologyManager,\
 net.floodlightcontroller.learningswitch.LearningSwitch,\
diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml
index 9f7e90e3519bd917dc45baa6834bf5f2f9701f5d..e4baf8bcf2d7714750c45adc6ad8298ad9e2f49b 100644
--- a/src/main/resources/logback-test.xml
+++ b/src/main/resources/logback-test.xml
@@ -4,8 +4,6 @@
       <pattern>%d{HH:mm:ss.SSS} %level [%logger{20}:%thread] %msg%n</pattern>
     </encoder>
   </appender>
-  <appender name="EV_WARN_ERR" class="net.floodlightcontroller.debugevent.DebugEventAppender">
-  </appender>
   <root level="DEBUG">
     <appender-ref ref="STDOUT" />
     <appender-ref ref="EV_WARN_ERR" />
@@ -29,8 +27,8 @@
   <logger name="net.floodlightcontroller.core.internal.OFSwitchManager" level="INFO"></logger>
   <logger name="net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler" level="INFO"></logger>
   <logger name="net.floodlightcontroller.core.internal.OFChannelHandler" level="INFO"></logger>
-  <logger name="net.floodlightcontroller.staticflowentry.StaticFlowEntries" level="INFO"></logger>
-  <logger name="net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher" level="INFO"></logger>
+  <logger name="net.floodlightcontroller.staticentry.StaticEntries" level="INFO"></logger>
+  <logger name="net.floodlightcontroller.staticentry.StaticEntryPusher" level="INFO"></logger>
   
   <logger name="net.floodlightcontroller.simpleft.FT" level="INFO"></logger>
   
diff --git a/src/main/resources/neutron.properties b/src/main/resources/neutron.properties
index 60683ca5864efe21d2965a51593789451ccc785d..b827f3299cefcdaf260afb7fa47760ce8f57640a 100644
--- a/src/main/resources/neutron.properties
+++ b/src/main/resources/neutron.properties
@@ -6,8 +6,7 @@ net.floodlightcontroller.core.internal.FloodlightProvider,\
 net.floodlightcontroller.threadpool.ThreadPool,\
 net.floodlightcontroller.debugcounter.DebugCounterServiceImpl,\
 net.floodlightcontroller.perfmon.PktInProcessingTime,\
-net.floodlightcontroller.debugevent.DebugEventService,\
-net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher,\
+net.floodlightcontroller.staticentry.StaticEntryPusher,\
 net.floodlightcontroller.restserver.RestApiServer,\
 net.floodlightcontroller.topology.TopologyManager,\
 net.floodlightcontroller.forwarding.Forwarding,\
diff --git a/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java
index 1053b1d63eaba4ef2b46b0eb5421af1579e7236b..7fa8150689f0054e21390bb4062f2df6ea4233a6 100644
--- a/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java
+++ b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java
@@ -45,15 +45,13 @@ import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.test.MockThreadPoolService;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.devicemanager.IEntityClassifierService;
 import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier;
 import net.floodlightcontroller.devicemanager.test.MockDeviceManager;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.restserver.RestApiServer;
-import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
+import net.floodlightcontroller.staticentry.StaticEntryPusher;
 import net.floodlightcontroller.storage.IResultSet;
 import net.floodlightcontroller.storage.IStorageSourceService;
 import net.floodlightcontroller.storage.memory.MemoryStorageSource;
@@ -78,7 +76,6 @@ public class ACLTest extends FloodlightTestCase {
 	protected FloodlightContext cntx;
 	protected IOFSwitch sw;
 	
-	private MockDebugEventService debugEventService; // dependency for device manager
 	private DefaultEntityClassifier entityClassifier; // dependency for device manager
 	private MockThreadPoolService tps; // dependency for device manager
 	private ITopologyService topology; // dependency for device manager
@@ -101,7 +98,6 @@ public class ACLTest extends FloodlightTestCase {
 		mockFloodlightProvider = getMockFloodlightProvider();
 		mockSwitchManager = getMockSwitchService();
 
-		debugEventService = new MockDebugEventService();
 		entityClassifier = new DefaultEntityClassifier();
 		tps = new MockThreadPoolService();
 		deviceManager = new MockDeviceManager();
@@ -128,7 +124,6 @@ public class ACLTest extends FloodlightTestCase {
 		fmc.addService(IOFSwitchService.class, mockSwitchManager);
 		fmc.addService(IDebugCounterService.class, debugCounterService);
 		fmc.addService(IStorageSourceService.class, storageService);
-		fmc.addService(IDebugEventService.class, debugEventService);
 		fmc.addService(IEntityClassifierService.class, entityClassifier);
 		fmc.addService(IThreadPoolService.class, tps);
 		fmc.addService(IDeviceService.class, deviceManager);
@@ -156,9 +151,9 @@ public class ACLTest extends FloodlightTestCase {
 		acl.startUp(fmc);
 		verify(topology);
 
-		storageService.createTable(StaticFlowEntryPusher.TABLE_NAME, null);
-		storageService.setTablePrimaryKeyName(StaticFlowEntryPusher.TABLE_NAME,
-				StaticFlowEntryPusher.COLUMN_NAME);
+		storageService.createTable(StaticEntryPusher.TABLE_NAME, null);
+		storageService.setTablePrimaryKeyName(StaticEntryPusher.TABLE_NAME,
+				StaticEntryPusher.Columns.COLUMN_NAME);
 
 	}
 
@@ -204,7 +199,7 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(), 1);
 
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -219,7 +214,7 @@ public class ACLTest extends FloodlightTestCase {
 		}
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -264,7 +259,7 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(), 2);
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -294,7 +289,7 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(), 3);
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_3_00:00:00:00:00:00:00:01");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_3_00:00:00:00:00:00:00:01");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -348,7 +343,7 @@ public class ACLTest extends FloodlightTestCase {
 				VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1));
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -367,7 +362,7 @@ public class ACLTest extends FloodlightTestCase {
 				VlanVid.ZERO, IPv4Address.of("10.0.0.2"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(2));
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
 		it = resultSet.iterator();
 		int count = 0;
 		while(it.hasNext()){
@@ -394,7 +389,7 @@ public class ACLTest extends FloodlightTestCase {
 				VlanVid.ZERO, IPv4Address.of("10.0.0.3"), IPv6Address.NONE, DatapathId.of(2), OFPort.of(1));
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -446,7 +441,7 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(), 1);
 
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
 		it = resultSet.iterator();
 		assertEquals(it.hasNext(), false);
 		
@@ -455,7 +450,7 @@ public class ACLTest extends FloodlightTestCase {
 				VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1));
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -511,7 +506,7 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(), 1);
 
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -526,7 +521,7 @@ public class ACLTest extends FloodlightTestCase {
 		}
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -545,12 +540,12 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(),0);
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
 		it = resultSet.iterator();
 		assertEquals(it.hasNext(), false);
 
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
 		it = resultSet.iterator();
 		assertEquals(it.hasNext(), false);
 		
@@ -597,7 +592,7 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(), 1);
 
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -612,7 +607,7 @@ public class ACLTest extends FloodlightTestCase {
 		}
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -639,7 +634,7 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(), 2);
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
 		it = resultSet.iterator();
 		while(it.hasNext()){
 			row = it.next().getRow();
@@ -658,17 +653,17 @@ public class ACLTest extends FloodlightTestCase {
 		assertEquals(acl.getRules().size(),0);
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
 		it = resultSet.iterator();
 		assertEquals(it.hasNext(), false);
 
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
 		it = resultSet.iterator();
 		assertEquals(it.hasNext(), false);
 		
 		resultSet = storageService.getRow(
-				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
+				StaticEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
 		it = resultSet.iterator();
 		assertEquals(it.hasNext(), false);
 		
diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
index 669876e7904ba16f5db21284869fc387724be6d3..c7171c732a1374ef78e1fc5c5177a52ff37bfa7b 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
@@ -41,8 +41,6 @@ import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.test.MockThreadPoolService;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl;
-import net.floodlightcontroller.debugevent.DebugEventService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
 import net.floodlightcontroller.packet.ARP;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPacket;
@@ -134,9 +132,6 @@ public class ControllerTest extends FloodlightTestCase {
         DebugCounterServiceImpl debugCounterService = new DebugCounterServiceImpl();
         fmc.addService(IDebugCounterService.class, debugCounterService);
 
-        DebugEventService debugEventService = new DebugEventService();
-        fmc.addService(IDebugEventService.class, debugEventService);
-
         IShutdownService shutdownService = createMock(IShutdownService.class);
         shutdownService.registerShutdownListener(anyObject(IShutdownListener.class));
         expectLastCall().anyTimes();
@@ -156,7 +151,6 @@ public class ControllerTest extends FloodlightTestCase {
         memstorage.init(fmc);
         tp.init(fmc);
         debugCounterService.init(fmc);
-        debugEventService.init(fmc);
         syncService.init(fmc);
         cm.init(fmc);
 
@@ -166,7 +160,6 @@ public class ControllerTest extends FloodlightTestCase {
         memstorage.startUp(fmc);
         tp.startUp(fmc);
         debugCounterService.startUp(fmc);
-        debugEventService.startUp(fmc);
         syncService.startUp(fmc);
         cm.startUp(fmc);
 
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java
index 538a8a9b3f6cd8ddc2db6c5a7ad927f1a1e78a92..944a49c6f27a778c772c6da57d8e95fe96e2a44b 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java
@@ -52,6 +52,7 @@ import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
+import org.projectfloodlight.openflow.protocol.OFFlowRemovedReason;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
 import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
 import org.projectfloodlight.openflow.protocol.OFMessage;
@@ -397,7 +398,7 @@ public class OFChannelHandlerVer10Test {
 
 
         // Send packet in. expect dispatch
-        OFFlowRemoved flowRemoved = factory.buildFlowRemoved()
+        OFFlowRemoved flowRemoved = factory.buildFlowRemoved().setReason(OFFlowRemovedReason.DELETE)
                 .build();
 
         resetAndExpectConnectionListener(flowRemoved);
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java
index 8989aeaed0ac9bc1a267ef467784efd4b039c974..ecb039a4fae124f3c269e7d47613e41b0ef5e55a 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java
@@ -53,6 +53,7 @@ import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
+import org.projectfloodlight.openflow.protocol.OFFlowRemovedReason;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
 import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
 import org.projectfloodlight.openflow.protocol.OFMessage;
@@ -400,6 +401,7 @@ public class OFChannelHandlerVer13Test {
 
 		// Send packet in. expect dispatch
 		OFFlowRemoved flowRemoved = factory.buildFlowRemoved()
+				.setReason(OFFlowRemovedReason.DELETE)
 				.build();
 
 		resetAndExpectConnectionListener(flowRemoved);
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java
index 9f254d658bbe130ac27394707bd8fd619e825218..e618b7c370a4e1238c35769ab38d973788b85de2 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java
@@ -56,6 +56,7 @@ import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+import org.projectfloodlight.openflow.protocol.OFFlowRemovedReason;
 import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
@@ -455,7 +456,7 @@ public abstract class OFSwitchHandlerTestBase {
 		// need to build/set this match object
 
 		Match match = factory.buildMatch().build();
-		OFMessage m = factory.buildFlowRemoved().setMatch(match).build();
+		OFMessage m = factory.buildFlowRemoved().setReason(OFFlowRemovedReason.DELETE).setMatch(match).build();
 		resetToStrict(sw);
 		sw.processDriverHandshakeMessage(m);
 		expectLastCall().once();
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java
index 7f8393f65b704c26e3b865c93a2518494439e7f3..69ef2d73bcea06aff931771246a3632c98ff1148 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java
@@ -106,7 +106,7 @@ public class OFSwitchHandshakeHandlerVer13Test extends OFSwitchHandlerTestBase {
         sw.setPortDescStats(anyObject(OFPortDescStatsReply.class));
         expectLastCall().once();
        
-        expect(sw.getOFFactory()).andReturn(factory).once();
+        expect(sw.getOFFactory()).andReturn(factory).anyTimes();
         replay(sw);
 
         reset(switchManager);
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java
index 6f5312ae2670344d91791bd4b87292a4ee5078e1..396f7182980e29b66a21b0d7e4d0fc65c41310ff 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java
@@ -55,8 +55,6 @@ import net.floodlightcontroller.core.SwitchDescription;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.DebugEventService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.restserver.RestApiServer;
 import net.floodlightcontroller.storage.IStorageSourceService;
@@ -119,9 +117,6 @@ public class OFSwitchManagerTest{
         MockDebugCounterService debugCounterService = new MockDebugCounterService();
         fmc.addService(IDebugCounterService.class, debugCounterService);
 
-        DebugEventService debugEventService = new DebugEventService();
-        fmc.addService(IDebugEventService.class, debugEventService);
-
         switchManager = new OFSwitchManager();
         fmc.addService(IOFSwitchService.class, switchManager);
 
@@ -140,7 +135,6 @@ public class OFSwitchManagerTest{
         switchManager.init(fmc);
         debugCounterService.init(fmc);
         memstorage.init(fmc);
-        debugEventService.init(fmc);
         restApi.init(fmc);
         cm.init(fmc);
 
@@ -148,7 +142,6 @@ public class OFSwitchManagerTest{
         switchManager.startUpBase(fmc);
         debugCounterService.startUp(fmc);
         memstorage.startUp(fmc);
-        debugEventService.startUp(fmc);
         threadPool.startUp(fmc);
         restApi.startUp(fmc);
         cm.startUp(fmc);
diff --git a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
index 7e502871302d7be19c2f804cdda54a1c1de1cd75..af3aa164a9f90c84aef6712fa08e09dd1c5c4351 100644
--- a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
+++ b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
@@ -27,7 +27,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ConcurrentMap;
@@ -36,7 +35,6 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
-import io.netty.util.Timer;
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.HAListenerTypeMarker;
 import net.floodlightcontroller.core.HARole;
@@ -60,8 +58,6 @@ import net.floodlightcontroller.core.util.ListenerDispatcher;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
 import org.projectfloodlight.openflow.protocol.OFType;
-import org.projectfloodlight.openflow.types.IPv4Address;
-import org.projectfloodlight.openflow.types.TransportPort;
 
 import net.floodlightcontroller.packet.Ethernet;
 
@@ -77,8 +73,6 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
     protected ConcurrentMap<OFType, ListenerDispatcher<OFType,IOFMessageListener>> listeners;
     protected ListenerDispatcher<HAListenerTypeMarker, IHAListener> haListeners;
     private HARole role;
-    private final Set<IPv4Address> openFlowHostname = Collections.singleton(IPv4Address.of("127.0.0.1"));
-    private final TransportPort openFlowPort = TransportPort.of(6653);
     private final boolean useAsyncUpdates;
     private volatile ExecutorService executorService;
     private volatile Future<?> mostRecentUpdateFuture;
@@ -386,27 +380,12 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
         return 1000000L;
     }
 
-    @Override
-    public Set<IPv4Address> getOFAddresses() {
-        return openFlowHostname;
-    }
-
-    @Override
-    public TransportPort getOFPort() {
-        return openFlowPort;
-    }
-
     @Override
     public void handleMessage(IOFSwitch sw, OFMessage m,
                               FloodlightContext bContext) {
         // do nothing
     }
 
-    @Override
-    public Timer getTimer() {
-        return null;
-    }
-
     @Override
     public RoleManager getRoleManager() {
         return null;
@@ -422,16 +401,6 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
         return null;
     }
 
-    @Override
-    public Set<String> getUplinkPortPrefixSet() {
-        return null;
-    }
-
-    @Override
-    public int getWorkerThreads() {
-        return 0;
-    }
-
     // paag
 	@Override
 	public void addCompletionListener(IControllerCompletionListener listener) {
@@ -443,4 +412,4 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
 	public void removeCompletionListener(IControllerCompletionListener listener) {
 		completionListeners.remove(listener);
 	}
-}
+}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java b/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java
index 6e8f51ccfb243880ffd689ec3428e197fbbcc1be..87915640c2464515a4f72302b188e57af268410f 100644
--- a/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java
+++ b/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java
@@ -28,7 +28,6 @@ import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
-import net.floodlightcontroller.core.rest.SwitchRepresentation;
 import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl;
 
 import org.projectfloodlight.openflow.protocol.OFFactory;
@@ -232,28 +231,4 @@ public class MockSwitchManager implements IFloodlightModule, IOFSwitchManager, I
     public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
         // do nothing
     }
-
-    @Override
-    public List<SwitchRepresentation> getSwitchRepresentations() {
-        List<SwitchRepresentation> representations = new ArrayList<SwitchRepresentation>();
-
-        for(DatapathId dpid : this.switches.keySet()) {
-            SwitchRepresentation representation = getSwitchRepresentation(dpid);
-            if(representation != null) {
-                representations.add(representation);
-            }
-        }
-        return representations;
-    }
-
-    @Override
-    public SwitchRepresentation getSwitchRepresentation(DatapathId dpid) {
-        IOFSwitch sw = this.switches.get(dpid);
-        OFSwitchHandshakeHandler handler = this.switchHandlers.get(dpid);
-
-        if(sw != null && handler != null) {
-            return new SwitchRepresentation(sw, handler);
-        }
-        return null;
-    }
 }
diff --git a/src/test/java/net/floodlightcontroller/debugcounter/OFConnectionCountersTest.java b/src/test/java/net/floodlightcontroller/debugcounter/OFConnectionCountersTest.java
index 46984522e60e9af43a471a42e8cc5c6f759c3deb..27083dff9b5546950971d5d36ae79402ccb42c9e 100644
--- a/src/test/java/net/floodlightcontroller/debugcounter/OFConnectionCountersTest.java
+++ b/src/test/java/net/floodlightcontroller/debugcounter/OFConnectionCountersTest.java
@@ -30,6 +30,7 @@ import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFFeaturesRequest;
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
+import org.projectfloodlight.openflow.protocol.OFFlowRemovedReason;
 import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
 import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
 import org.projectfloodlight.openflow.protocol.OFGroupMod;
@@ -38,6 +39,7 @@ import org.projectfloodlight.openflow.protocol.OFHello;
 import org.projectfloodlight.openflow.protocol.OFHelloElem;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFMeterMod;
+import org.projectfloodlight.openflow.protocol.OFMeterModCommand;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
 import org.projectfloodlight.openflow.protocol.OFPacketInReason;
 import org.projectfloodlight.openflow.protocol.OFPacketOut;
@@ -92,10 +94,10 @@ public class OFConnectionCountersTest extends FloodlightTestCase {
 
     @Test
     public void TestConnectionCounterRegistered(){
-        for(OFType oft : OFType.values()){
+        for (OFType oft : OFType.values()){
             boolean found = false;
-            for(DebugCounterResource dcInfo : dc.getAllCounterValues()){
-                if(dcInfo.getCounterHierarchy().contains(oft.toString())){
+            for (DebugCounterResource dcInfo : dc.getAllCounterValues()){
+                if (dcInfo.getCounterHierarchy().contains(oft.toString())){
                     found = true;
                 }
             }
@@ -108,9 +110,9 @@ public class OFConnectionCountersTest extends FloodlightTestCase {
      * @param typeStr Type of message as a string
      * @param value Expected value of the counter
      */
-    public void validateCounter(String typeStr,long value){
-        for(DebugCounterResource dcInfo : dc.getAllCounterValues()){
-            if(dcInfo.getCounterHierarchy().contains("/"+typeStr)){
+    public void validateCounter(String typeStr, long value){
+        for (DebugCounterResource dcInfo : dc.getAllCounterValues()){
+            if (dcInfo.getCounterHierarchy().contains("/"+typeStr)){
                 assertEquals( Long.valueOf(value), dcInfo.getCounterValue());
             }
         }
@@ -189,7 +191,7 @@ public class OFConnectionCountersTest extends FloodlightTestCase {
 
         //Flow Removed
         //
-        OFFlowRemoved flowRemMsg = factory.buildFlowRemoved().build();
+        OFFlowRemoved flowRemMsg = factory.buildFlowRemoved().setReason(OFFlowRemovedReason.DELETE).build();
         updateAndTestCounter(flowRemMsg,OFType.FLOW_REMOVED.toString());
 
 
@@ -231,7 +233,7 @@ public class OFConnectionCountersTest extends FloodlightTestCase {
 
         // meter mod
         //
-        OFMeterMod meterModMsg = factory.buildMeterMod().build();
+        OFMeterMod meterModMsg = factory.buildMeterMod().setCommand(OFMeterModCommand.ADD).build();
         updateAndTestCounter(meterModMsg,OFType.METER_MOD.toString());
 
         // Packet in
@@ -330,6 +332,7 @@ public class OFConnectionCountersTest extends FloodlightTestCase {
         //
         OFRoleStatus roleStatus = factory.buildRoleStatus()
         		.setReason(OFControllerRoleReason.CONFIG)
+        		.setRole(OFControllerRole.ROLE_MASTER)
         		.build();
         updateAndTestCounter(roleStatus,OFType.ROLE_STATUS.toString());
         
diff --git a/src/test/java/net/floodlightcontroller/debugevent/DebugEventTest.java b/src/test/java/net/floodlightcontroller/debugevent/DebugEventTest.java
deleted file mode 100644
index aa2acfaeae37c128e9ebaac63658fa0a8454d236..0000000000000000000000000000000000000000
--- a/src/test/java/net/floodlightcontroller/debugevent/DebugEventTest.java
+++ /dev/null
@@ -1,234 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.easymock.EasyMock.anyObject;
-
-import org.easymock.EasyMock;
-import org.junit.Before;
-import org.junit.Test;
-import net.floodlightcontroller.core.IShutdownListener;
-import net.floodlightcontroller.core.IShutdownService;
-import net.floodlightcontroller.core.module.FloodlightModuleContext;
-import net.floodlightcontroller.debugevent.DebugEventResource.EventInfoResource;
-import net.floodlightcontroller.debugevent.EventResource.Metadata;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventType;
-import org.projectfloodlight.openflow.types.DatapathId;
-import net.floodlightcontroller.test.FloodlightTestCase;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class DebugEventTest extends FloodlightTestCase {
-    DebugEventService debugEvent;
-    protected static Logger log = LoggerFactory.getLogger(DebugEventTest.class);
-
-    @Override
-    @Before
-    public void setUp() throws Exception {
-        debugEvent = new DebugEventService();
-        FloodlightModuleContext fmc = new FloodlightModuleContext();
-        IShutdownService shutdownService =
-                EasyMock.createMock(IShutdownService.class);
-        shutdownService.registerShutdownListener(anyObject(IShutdownListener.class));
-        EasyMock.expectLastCall().once();
-        EasyMock.replay(shutdownService);
-        fmc.addService(IShutdownService.class, shutdownService);
-        debugEvent.startUp(fmc);
-        EasyMock.verify(shutdownService);
-    }
-
-
-    @Test
-    public void testRegisterAndUpdateEvent() throws Exception {
-        assertEquals(0, debugEvent.currentEvents.size());
-        IEventCategory<SwitchyEvent> event1 = null;
-        IEventCategory<PacketyEvent> event2 = null;
-        event1 = debugEvent.buildEvent(SwitchyEvent.class)
-                .setModuleName("dbgevtest")
-                .setEventName("switchevent")
-                .setEventType(EventType.ALWAYS_LOG)
-                .setBufferCapacity(100)
-                .setAckable(false)
-                .register();
-        event2 = debugEvent.buildEvent(PacketyEvent.class)
-                .setModuleName("dbgevtest")
-                .setEventName("pktinevent")
-                .setEventType(EventType.ALWAYS_LOG)
-                .setBufferCapacity(100)
-                .setAckable(false)
-                .register();
-
-        assertEquals(2, debugEvent.currentEvents.size());
-        assertTrue(null != debugEvent.moduleEvents.get("dbgevtest").
-                                                     get("switchevent"));
-        int eventId1 = debugEvent.moduleEvents.get("dbgevtest").
-                                                     get("switchevent");
-        assertTrue(null != debugEvent.moduleEvents.get("dbgevtest").
-                                                     get("pktinevent"));
-        int eventId2 = debugEvent.moduleEvents.get("dbgevtest").
-                                                     get("pktinevent");
-        assertEquals(true, debugEvent.containsModuleName("dbgevtest"));
-        assertEquals(true, debugEvent.containsModuleEventName("dbgevtest","switchevent"));
-        assertEquals(true, debugEvent.containsModuleEventName("dbgevtest","pktinevent"));
-
-        assertEquals(0, debugEvent.allEvents.get(eventId1).circularEventBuffer.size());
-        assertEquals(0, debugEvent.allEvents.get(eventId2).circularEventBuffer.size());
-
-        // update is immediately flushed to global store
-        event1.newEventWithFlush(new SwitchyEvent(DatapathId.of(1L), "connected"));
-        assertEquals(1, debugEvent.allEvents.get(eventId1).circularEventBuffer.size());
-
-        // update is flushed only when flush is explicitly called
-        event2.newEventNoFlush(new PacketyEvent(DatapathId.of(1L), 24L));
-        assertEquals(0, debugEvent.allEvents.get(eventId2).circularEventBuffer.size());
-
-        debugEvent.flushEvents();
-        assertEquals(1, debugEvent.allEvents.get(eventId1).circularEventBuffer.size());
-        assertEquals(1, debugEvent.allEvents.get(eventId2).circularEventBuffer.size());
-
-        EventInfoResource de = debugEvent.getSingleEventHistory("dbgevtest","switchevent", 100);
-        assertEquals(1, de.events.size());
-        assertTrue(de.events.get(0).getDataFields().contains(new Metadata("dpid", "00:00:00:00:00:00:00:01")));
-        assertTrue(de.events.get(0).getDataFields().contains(new Metadata("reason", "connected")));
-
-        EventInfoResource de2 = debugEvent.getSingleEventHistory("dbgevtest","pktinevent", 100);
-        assertEquals(1, de2.events.size());
-        assertTrue(de2.events.get(0).getDataFields().contains(new Metadata("dpid", "00:00:00:00:00:00:00:01")));
-        assertTrue(de2.events.get(0).getDataFields().contains(new Metadata("srcMac", "00:00:00:00:00:18")));
-    }
-
-    public class SwitchyEvent {
-        @EventColumn(name = "dpid", description = EventFieldType.DPID)
-        DatapathId dpid;
-
-        @EventColumn(name = "reason", description = EventFieldType.STRING)
-        String reason;
-
-        public SwitchyEvent(DatapathId dpid, String reason) {
-            this.dpid = dpid;
-            this.reason = reason;
-        }
-    }
-
-    public class PacketyEvent {
-        @EventColumn(name = "dpid", description = EventFieldType.DPID)
-        DatapathId dpid;
-
-        @EventColumn(name = "srcMac", description = EventFieldType.MAC)
-        long mac;
-
-        public PacketyEvent(DatapathId dpid, long mac) {
-            this.dpid = dpid;
-            this.mac = mac;
-        }
-    }
-
-    public class IntEvent {
-        @EventColumn(name = "index", description = EventFieldType.PRIMITIVE)
-        int index;
-
-        public IntEvent(int i) {
-            this.index = i;
-        }
-
-        @Override
-        public String toString() {
-            return String.valueOf(index);
-        }
-    }
-
-    @Test
-    public void testEventCyclesWithFlush() throws Exception {
-        IEventCategory<IntEvent> ev = null;
-        ev = debugEvent.buildEvent(IntEvent.class)
-                .setModuleName("test")
-                .setEventName("int")
-                .setEventDescription("just a test")
-                .setEventType(EventType.ALWAYS_LOG)
-                .setBufferCapacity(20)
-                .setAckable(false)
-                .register();
-
-        for (int i=0; i<20; i++)
-            ev.newEventWithFlush(new IntEvent(i));
-        int i=19;
-        EventInfoResource dei = debugEvent.getSingleEventHistory("test","int", 100);
-        for (EventResource m : dei.events) {
-            assertTrue(m.getDataFields().get(0).getEventData().equals(String.valueOf(i)));
-            i--;
-        }
-        for (int j= 500; j<550; j++)
-            ev.newEventWithFlush(new IntEvent(j));
-        int k=549;
-        dei = debugEvent.getSingleEventHistory("test","int", 100);
-        for (EventResource m : dei.events) {
-            //log.info("{}", m.get("index"));
-            assertTrue(m.getDataFields().get(0).getEventData().equals(String.valueOf(k)));
-            k--;
-        }
-    }
-
-
-    @Test
-    public void testEventCyclesNoFlush() throws Exception {
-        IEventCategory<IntEvent> ev = null;
-        ev = debugEvent.buildEvent(IntEvent.class)
-                .setModuleName("test")
-                .setEventName("int")
-                .setEventDescription("just a test")
-                .setEventType(EventType.ALWAYS_LOG)
-                .setBufferCapacity(20)
-                .setAckable(false)
-                .register();
-
-        // flushes when local buffer fills up
-        for (int i=0; i<20; i++)
-            ev.newEventNoFlush(new IntEvent(i));
-        int i=19;
-        EventInfoResource dei = debugEvent.getSingleEventHistory("test","int", 0);
-        for (EventResource m : dei.events) {
-            assertTrue(m.getDataFields().get(0).getEventData().equals(String.valueOf(i)));
-            i--;
-        }
-        //log.info("done with first bunch");
-        // flushes when local buffer fills up or when flushEvents is explicitly called
-        for (int j= 500; j<550; j++) {
-            ev.newEventNoFlush(new IntEvent(j));
-            //if (j == 515)
-                //debugEvent.flushEvents();
-        }
-        debugEvent.flushEvents();
-
-        int k=549;
-        dei = debugEvent.getSingleEventHistory("test","int", 100);
-        for (EventResource m : dei.events) {
-            //log.info("{}", m.get("index"));
-            assertTrue(m.getDataFields().get(0).getEventData().equals(String.valueOf(k)));
-            k--;
-        }
-    }
-
-    @Test
-    public void testAckEvent() throws Exception{
-        IEventCategory<IntEvent> ev = null;
-        ev = debugEvent.buildEvent(IntEvent.class)
-                .setModuleName("test")
-                .setEventName("ack")
-                .setEventDescription("just a test")
-                .setEventType(EventType.ALWAYS_LOG)
-                .setBufferCapacity(20)
-                .setAckable(false)
-                .register();
-        //create a single event
-        IntEvent e = new IntEvent(10);
-        ev.newEventWithFlush(e);
-        EventInfoResource dei = debugEvent.getSingleEventHistory("test","ack", 1);
-        debugEvent.setAck(dei.getEventId(),
-                          dei.getEvents().get(0).getEventInstanceId(),
-                          true);
-        dei = debugEvent.getSingleEventHistory("test","ack", 1);
-        assertTrue(dei.getEvents().get(0).isAcked());
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/debugevent/EventTest.java b/src/test/java/net/floodlightcontroller/debugevent/EventTest.java
deleted file mode 100644
index 9ae64f5c537be8442e8ede113a2b41158fa02ad3..0000000000000000000000000000000000000000
--- a/src/test/java/net/floodlightcontroller/debugevent/EventTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package net.floodlightcontroller.debugevent;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-import net.floodlightcontroller.debugevent.Event;
-import net.floodlightcontroller.debugevent.EventResource;
-import net.floodlightcontroller.debugevent.EventResource.EventResourceBuilder;
-import net.floodlightcontroller.debugevent.EventResource.Metadata;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
-import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType;
-
-import org.projectfloodlight.openflow.types.DatapathId;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class EventTest {
-    protected static Logger log = LoggerFactory.getLogger(EventTest.class);
-
-    @Test
-    public void testFormat() {
-        River r = new River("ganges", 42);
-
-        Event e = new Event(1L, 32, "test",
-                      new RiverEvent(DatapathId.of(1L), (short)10, true, "big river", 5, 4L, r), 10L);
-
-        EventResourceBuilder edb = new EventResourceBuilder();
-        edb.dataFields.add(new Metadata("dpid", "00:00:00:00:00:00:00:01"));
-        edb.dataFields.add(new Metadata("portId", "10"));
-        edb.dataFields.add(new Metadata("valid", "true"));
-        edb.dataFields.add(new Metadata("desc", "big river"));
-        edb.dataFields.add(new Metadata("ip", "0.0.0.5"));
-        edb.dataFields.add(new Metadata("mac", "00:00:00:00:00:04"));
-        edb.dataFields.add(new Metadata("obj", "ganges/42"));
-        edb.setThreadId(e.getThreadId());
-        edb.setThreadName(e.getThreadName());
-        edb.setTimeStamp(e.getTimeMs());
-        edb.setModuleEventName("test");
-        EventResource ed = edb.build();
-
-        // check Event.getFormattedEvent()
-        assertTrue(ed.equals(e.getFormattedEvent(RiverEvent.class, "test")));
-
-        /* Why does it matter? Java's built-in Date does not format in ISO8601...
-        // ensure timestamp comes in ISO8601 time
-        // e.g.: 1969-12-31T16:00:00.001-08:00
-        Pattern pat =
-                Pattern.compile("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}[+-]\\d{2}:\\d{2}");
-        Date t1 = e.getFormattedEvent(RiverEvent.class, "test2").getTimestamp();
-        Matcher m1 = pat.matcher(t1.toString());
-        assertTrue(m1.matches());
-        */
-
-        // ensure that cached value is not returned for incorrect class
-        assertFalse(ed.equals(e.getFormattedEvent(River.class, "test")));
-
-        assertTrue(e.getFormattedEvent(River.class, "test").getDataFields().
-                   contains(new Metadata("Error",
-                   "null event data or event-class does not match event-data")));
-        assertTrue(e.getFormattedEvent(null, "test").getDataFields().contains(
-          new Metadata("Error",
-                   "null event data or event-class does not match event-data")));
-    }
-
-    @Test
-    public void testIncorrectAnnotation() {
-        Event e = new Event(1L, 32, "test",
-                            new LakeEvent(199), 11L); // dpid cannot be int
-        assertTrue(e.getFormattedEvent(LakeEvent.class, "test").getDataFields()
-          .contains(new Metadata("Error",
-                             "java.lang.Integer cannot be cast to org.projectfloodlight.openflow.types.DatapathId")));
-
-        Event e2 = new Event(1L, 32, "test",
-                            new LakeEvent2(199), 12L); // mac cannot be int
-        assertTrue(e2.getFormattedEvent(LakeEvent2.class, "test").getDataFields()
-                   .contains(new Metadata("Error",
-                                      "java.lang.Integer cannot be cast to java.lang.Long")));
-    }
-
-    class RiverEvent  {
-        @EventColumn(name = "dpid", description = EventFieldType.DPID)
-        DatapathId dpid;
-
-        @EventColumn(name = "portId", description = EventFieldType.PRIMITIVE)
-        short srcPort;
-
-        @EventColumn(name = "valid", description = EventFieldType.PRIMITIVE)
-        boolean isValid;
-
-        @EventColumn(name = "desc", description = EventFieldType.STRING)
-        String desc;
-
-        @EventColumn(name = "ip", description = EventFieldType.IPv4)
-        int ipAddr;
-
-        @EventColumn(name = "mac", description = EventFieldType.MAC)
-        long macAddr;
-
-        @EventColumn(name = "obj", description = EventFieldType.OBJECT)
-        River amazon;
-
-        // Instances of RiverEvent ensure that that any internal object
-        // (eg. River instances) has been copied before it is given to DebugEvents.
-        public RiverEvent(DatapathId dpid, short srcPort, boolean isValid,
-                            String desc, int ip, long mac, River passedin) {
-            this.dpid = dpid;
-            this.srcPort = srcPort;
-            this.isValid = isValid;
-            this.desc = desc;
-            this.ipAddr = ip;
-            this.macAddr = mac;
-            this.amazon = new River(passedin); // invoke copy constructor
-        }
-    }
-
-    // Object of the River class will be passed in as part of the EventExample instance
-    // The user needs to ensure that the River class has a copy constructor
-    // and it overrides the toString method.
-    class River {
-        String r1;
-        long r2;
-
-        public River(String r1, long r2) {
-            this.r1 = r1;
-            this.r2 = r2;
-        }
-        // should have copy constructor
-        public River(River passedin) {
-            this.r1 = passedin.r1;
-            this.r2 = passedin.r2;
-        }
-        // needs to override toString method
-        @Override
-        public String toString() {
-            return (r1 + "/" + r2);
-        }
-    }
-
-
-    class LakeEvent {
-        @EventColumn(name = "dpid", description = EventFieldType.DPID)
-        int dpid;
-
-        public LakeEvent(int dpid) {
-            this.dpid = dpid;
-        }
-    }
-
-    class LakeEvent2 {
-        @EventColumn(name = "mac", description = EventFieldType.MAC)
-        int mac;
-
-        public LakeEvent2(int mac) {
-            this.mac = mac;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
index a9a29650a0be948df29727a1a578021b3c311f84..3c5d6fea36af340530b01260d1a0c1b2bcad66aa 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
@@ -59,8 +59,6 @@ import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.test.MockThreadPoolService;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.devicemanager.IDevice;
 import net.floodlightcontroller.devicemanager.IDeviceListener;
 import net.floodlightcontroller.devicemanager.IDeviceService;
@@ -134,7 +132,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 	DeviceManagerImpl deviceManager;
 	MemoryStorageSource storageSource;
 	IDebugCounterService debugCounterService;
-	IDebugEventService debugEventService;
 
 	private IOFSwitch makeSwitchMock(DatapathId id) {
 		IOFSwitch mockSwitch = createMock(IOFSwitch.class);
@@ -187,8 +184,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		mockFloodlightProvider = getMockFloodlightProvider();
 		mockFloodlightProvider.setRole(initialRole, "");
 		debugCounterService = new MockDebugCounterService();
-		debugEventService = new MockDebugEventService();
-
 
 		deviceManager = new DeviceManagerImpl();
 		DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier();
@@ -201,7 +196,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 		fmc.addService(ITopologyService.class, topology);
 		fmc.addService(ISyncService.class, syncService);
 		fmc.addService(IDebugCounterService.class, debugCounterService);
-		fmc.addService(IDebugEventService.class, debugEventService);
 		tp.init(fmc);
 		restApi.init(fmc);
 		storageSource.init(fmc);
diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
index 7ff102fa2bed58b96ab65be183e041f320676e90..9d68ba9f98a4309d13fda155978e799f12b083db 100644
--- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
+++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
@@ -38,8 +38,6 @@ import net.floodlightcontroller.core.test.MockThreadPoolService;
 import net.floodlightcontroller.core.types.NodePortTuple;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier;
 import net.floodlightcontroller.devicemanager.test.MockDeviceManager;
 import net.floodlightcontroller.devicemanager.IDevice;
@@ -149,7 +147,6 @@ public class ForwardingTest extends FloodlightTestCase {
 		fmc.addService(IEntityClassifierService.class, entityClassifier);
 		fmc.addService(ISyncService.class, mockSyncService);
 		fmc.addService(IDebugCounterService.class, new MockDebugCounterService());
-		fmc.addService(IDebugEventService.class, new MockDebugEventService());
 		fmc.addService(IOFSwitchService.class, getMockSwitchService());
 		fmc.addService(ILinkDiscoveryService.class, linkService);
 
diff --git a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java
index 6cc84cb745cb7f26c2b920c4880d5913d6df249c..7ba7ee0d417ddf802f31bbbf1d5385f583e52711 100644
--- a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java
+++ b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java
@@ -46,8 +46,6 @@ import net.floodlightcontroller.core.test.MockThreadPoolService;
 import net.floodlightcontroller.core.types.NodePortTuple;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService;
 import net.floodlightcontroller.packet.Data;
@@ -141,7 +139,6 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase {
         TopologyManager routingEngine = new TopologyManager();
         ldm.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>();
         IDebugCounterService debugCounterService = new MockDebugCounterService();
-        IDebugEventService debugEventService = new MockDebugEventService();
         MockThreadPoolService tp = new MockThreadPoolService();
         RestApiServer restApi = new RestApiServer();
         MemoryStorageSource storageService = new MemoryStorageSource();
@@ -153,7 +150,6 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase {
         cntx.addService(IStorageSourceService.class, storageService);
         cntx.addService(IFloodlightProviderService.class, getMockFloodlightProvider());
         cntx.addService(IDebugCounterService.class, debugCounterService);
-        cntx.addService(IDebugEventService.class, debugEventService);
         cntx.addService(IOFSwitchService.class, getMockSwitchService());
         restApi.init(cntx);
         tp.init(cntx);
diff --git a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
index b0496135e6ceb4c3f90748454b6dd4a9944580bf..09bfec33a7769a78c7896a7204cf0de561b30868 100644
--- a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
+++ b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
@@ -39,7 +39,6 @@ import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 import org.projectfloodlight.openflow.protocol.OFFactory;
-import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
@@ -57,7 +56,6 @@ import org.projectfloodlight.openflow.types.VlanVid;
 import org.projectfloodlight.openflow.protocol.OFPacketInReason;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
-import org.projectfloodlight.openflow.util.HexString;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.sdnplatform.sync.ISyncService;
 import org.sdnplatform.sync.test.MockSyncService;
@@ -71,8 +69,6 @@ import net.floodlightcontroller.core.test.MockThreadPoolService;
 import net.floodlightcontroller.core.types.NodePortTuple;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.devicemanager.IEntityClassifierService;
 import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier;
@@ -86,8 +82,8 @@ import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.restserver.RestApiServer;
 import net.floodlightcontroller.routing.IRoutingService;
 import net.floodlightcontroller.routing.Route;
-import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
-import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
+import net.floodlightcontroller.staticentry.IStaticEntryPusherService;
+import net.floodlightcontroller.staticentry.StaticEntryPusher;
 import net.floodlightcontroller.storage.IStorageSourceService;
 import net.floodlightcontroller.storage.memory.MemoryStorageSource;
 import net.floodlightcontroller.test.FloodlightTestCase;
@@ -105,7 +101,7 @@ public class LoadBalancerTest extends FloodlightTestCase {
 	protected DefaultEntityClassifier entityClassifier;
 	protected IRoutingService routingEngine;
 	protected ITopologyService topology;
-	protected StaticFlowEntryPusher sfp;
+	protected StaticEntryPusher sfp;
 	protected MemoryStorageSource storage;
 	protected RestApiServer restApi;
 	protected VipsResource vipsResource;
@@ -113,7 +109,6 @@ public class LoadBalancerTest extends FloodlightTestCase {
 	protected MembersResource membersResource;
 	private MockSyncService mockSyncService;
 	protected IDebugCounterService debugCounterService;
-	protected IDebugEventService debugEventService;
 	protected LBVip vip1, vip2;
 	protected LBPool pool1, pool2, pool3;
 	protected LBMember member1, member2, member3, member4;
@@ -136,11 +131,10 @@ public class LoadBalancerTest extends FloodlightTestCase {
 		topology = createMock(ITopologyService.class);
 		routingEngine = createMock(IRoutingService.class);
 		restApi = new RestApiServer();
-		sfp = new StaticFlowEntryPusher();
+		sfp = new StaticEntryPusher();
 		storage = new MemoryStorageSource(); //dependency for sfp
 		mockSyncService = new MockSyncService();
 		debugCounterService = new MockDebugCounterService();
-		debugEventService = new MockDebugEventService();
 
 		fmc.addService(IRestApiService.class, restApi);
 		fmc.addService(IFloodlightProviderService.class, getMockFloodlightProvider());
@@ -149,12 +143,11 @@ public class LoadBalancerTest extends FloodlightTestCase {
 		fmc.addService(IDeviceService.class, deviceManager);
 		fmc.addService(ITopologyService.class, topology);
 		fmc.addService(IRoutingService.class, routingEngine);
-		fmc.addService(IStaticFlowEntryPusherService.class, sfp);
+		fmc.addService(IStaticEntryPusherService.class, sfp);
 		fmc.addService(ILoadBalancerService.class, lb);
 		fmc.addService(IStorageSourceService.class, storage);
 		fmc.addService(ISyncService.class, mockSyncService);
 		fmc.addService(IDebugCounterService.class, debugCounterService);
-		fmc.addService(IDebugEventService.class, debugEventService);
 		fmc.addService(IOFSwitchService.class, getMockSwitchService());
 
 		lb.init(fmc);
@@ -510,7 +503,7 @@ public class LoadBalancerTest extends FloodlightTestCase {
 		// Mock proxy arp packet-out
 		arpReply1 = new Ethernet()
 		.setSourceMACAddress(LBVip.LB_PROXY_MAC)
-		.setDestinationMACAddress(HexString.fromHexString("00:00:00:00:00:01"))
+		.setDestinationMACAddress(MacAddress.of("00:00:00:00:00:01"))
 		.setEtherType(EthType.ARP)
 		.setVlanID((short) 0)
 		.setPriorityCode((byte) 0)
@@ -686,7 +679,7 @@ public class LoadBalancerTest extends FloodlightTestCase {
 		assertTrue(msglist2.size()==2); // has inbound and outbound packetouts
 		// TODO: not seeing flowmods yet ...
 
-		Map<String, OFFlowMod> map = sfp.getFlows(DatapathId.of(1L));
+		Map<String, OFMessage> map = sfp.getEntries(DatapathId.of(1L));
 
 		assertTrue(map.size()==4);
 	}
diff --git a/src/test/java/net/floodlightcontroller/notification/MockNotificationManagerFactory.java b/src/test/java/net/floodlightcontroller/notification/MockNotificationManagerFactory.java
deleted file mode 100644
index 9e49c20161db7d1557ac76770a5f9e5989446681..0000000000000000000000000000000000000000
--- a/src/test/java/net/floodlightcontroller/notification/MockNotificationManagerFactory.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package net.floodlightcontroller.notification;
-
-public class MockNotificationManagerFactory implements
-    INotificationManagerFactory {
-
-    @Override
-    public <T> INotificationManager getNotificationManager(Class<T> clazz) {
-        return null;
-    }
-
-}
diff --git a/src/test/java/net/floodlightcontroller/notification/NotificationTest.java b/src/test/java/net/floodlightcontroller/notification/NotificationTest.java
deleted file mode 100644
index e9751fecf64d39b4d71af4c5ca58e2bf9610e97f..0000000000000000000000000000000000000000
--- a/src/test/java/net/floodlightcontroller/notification/NotificationTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package net.floodlightcontroller.notification;
-
-import static org.junit.Assert.*;
-
-import org.junit.AfterClass;
-import org.junit.Test;
-
-public class NotificationTest {
-
-    @Test
-    public void testDynamicBinding() {
-        System.setProperty(NotificationManagerFactory.NOTIFICATION_FACTORY_NAME,
-                           "net.floodlightcontroller.notification.MockNotificationManagerFactory");
-        NotificationManagerFactory.init();
-        INotificationManagerFactory factory =
-                NotificationManagerFactory.getNotificationManagerFactory();
-        assertNotNull(factory);
-        assertTrue(factory instanceof MockNotificationManagerFactory);        
-    }
-    
-    @AfterClass
-    public static void resetDefaultFactory() {
-        System.clearProperty(NotificationManagerFactory.NOTIFICATION_FACTORY_NAME);
-        NotificationManagerFactory.init();
-    }
-}
diff --git a/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java b/src/test/java/net/floodlightcontroller/staticentry/StaticFlowTests.java
similarity index 74%
rename from src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java
rename to src/test/java/net/floodlightcontroller/staticentry/StaticFlowTests.java
index fc0c1b27323e40cf2293064e8f603bec60c3fcc7..a2a6eb964d58d84c71b0d1d777927bb6859b6c3e 100644
--- a/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java
+++ b/src/test/java/net/floodlightcontroller/staticentry/StaticFlowTests.java
@@ -14,7 +14,7 @@
  *    under the License.
  **/
 
-package net.floodlightcontroller.staticflowentry;
+package net.floodlightcontroller.staticentry;
 
 import java.io.IOException;
 import java.util.Collections;
@@ -31,16 +31,21 @@ import org.easymock.EasyMock;
 import org.junit.Test;
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFFlowDelete;
+import org.projectfloodlight.openflow.protocol.OFFlowDeleteStrict;
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.match.MatchFields;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.util.HexString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IOFSwitch;
@@ -55,14 +60,15 @@ import net.floodlightcontroller.util.MatchUtils;
 import net.floodlightcontroller.util.OFMessageUtils;
 import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.restserver.RestApiServer;
-import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
+import net.floodlightcontroller.staticentry.StaticEntryPusher;
 import net.floodlightcontroller.storage.IStorageSourceService;
 import net.floodlightcontroller.storage.memory.MemoryStorageSource;
-import static net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher.*;
+import static net.floodlightcontroller.staticentry.StaticEntryPusher.*;
 import static org.easymock.EasyMock.*;
 import static org.junit.Assert.*;
 
 public class StaticFlowTests extends FloodlightTestCase {
+	protected static Logger log = LoggerFactory.getLogger(StaticFlowTests.class);
 
 	static String TestSwitch1DPID = "00:00:00:00:00:00:00:01";
 	static int TotalTestRules = 3;
@@ -78,15 +84,15 @@ public class StaticFlowTests extends FloodlightTestCase {
 	static {
 		FlowMod1 = factory.buildFlowModify().build();
 		TestRule1 = new HashMap<String,Object>();
-		TestRule1.put(COLUMN_NAME, "TestRule1");
-		TestRule1.put(COLUMN_SWITCH, TestSwitch1DPID);
+		TestRule1.put(Columns.COLUMN_NAME, "TestRule1");
+		TestRule1.put(Columns.COLUMN_SWITCH, TestSwitch1DPID);
 		// setup match
 		Match match;
-		TestRule1.put(COLUMN_DL_DST, "00:20:30:40:50:60");
+		TestRule1.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_DST), "00:20:30:40:50:60");
 		match = MatchUtils.fromString("eth_dst=00:20:30:40:50:60", factory.getVersion());
 		// setup actions
 		List<OFAction> actions = new LinkedList<OFAction>();
-		TestRule1.put(COLUMN_ACTIONS, "output=1");
+		TestRule1.put(Columns.COLUMN_ACTIONS, "output=1");
 		actions.add(factory.actions().output(OFPort.of(1), Integer.MAX_VALUE));
 		// done
 		FlowMod1 = FlowMod1.createBuilder().setMatch(match)
@@ -105,16 +111,16 @@ public class StaticFlowTests extends FloodlightTestCase {
 	static {
 		FlowMod2 = factory.buildFlowModify().build();
 		TestRule2 = new HashMap<String,Object>();
-		TestRule2.put(COLUMN_NAME, "TestRule2");
-		TestRule2.put(COLUMN_SWITCH, TestSwitch1DPID);
+		TestRule2.put(Columns.COLUMN_NAME, "TestRule2");
+		TestRule2.put(Columns.COLUMN_SWITCH, TestSwitch1DPID);
 		// setup match
 		Match match;        
-		TestRule2.put(COLUMN_DL_TYPE, "0x800");
-		TestRule2.put(COLUMN_NW_DST, "192.168.1.0/24");
+		TestRule2.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_TYPE), "0x800");
+		TestRule2.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.IPV4_DST), "192.168.1.0/24");
 		match = MatchUtils.fromString("eth_type=0x800,ipv4_dst=192.168.1.0/24", factory.getVersion());
 		// setup actions
 		List<OFAction> actions = new LinkedList<OFAction>();
-		TestRule2.put(COLUMN_ACTIONS, "output=1");
+		TestRule2.put(Columns.COLUMN_ACTIONS, "output=1");
 		actions.add(factory.actions().output(OFPort.of(1), Integer.MAX_VALUE));
 		// done
 		FlowMod2 = FlowMod2.createBuilder().setMatch(match)
@@ -130,7 +136,7 @@ public class StaticFlowTests extends FloodlightTestCase {
 
 	static Map<String,Object> TestRule3;
 	static OFFlowMod FlowMod3;
-	private StaticFlowEntryPusher staticFlowEntryPusher;
+	private StaticEntryPusher pusher;
 	private IOFSwitchService switchService;
 	private IOFSwitch mockSwitch;
 	private MockDebugCounterService debugCounterService;
@@ -141,21 +147,21 @@ public class StaticFlowTests extends FloodlightTestCase {
 	static {
 		FlowMod3 = factory.buildFlowModify().build();
 		TestRule3 = new HashMap<String,Object>();
-		TestRule3.put(COLUMN_NAME, "TestRule3");
-		TestRule3.put(COLUMN_SWITCH, TestSwitch1DPID);
+		TestRule3.put(Columns.COLUMN_NAME, "TestRule3");
+		TestRule3.put(Columns.COLUMN_SWITCH, TestSwitch1DPID);
 		// setup match
 		Match match;
-		TestRule3.put(COLUMN_DL_DST, "00:20:30:40:50:60");
-		TestRule3.put(COLUMN_DL_VLAN, 96);
+		TestRule3.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.ETH_DST), "00:20:30:40:50:60");
+		TestRule3.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.VLAN_VID), 96);
 		match = MatchUtils.fromString("eth_dst=00:20:30:40:50:60,eth_vlan_vid=96", factory.getVersion());
 		// setup actions
-		TestRule3.put(COLUMN_ACTIONS, "output=controller");
+		TestRule3.put(Columns.COLUMN_ACTIONS, "output=controller");
 		List<OFAction> actions = new LinkedList<OFAction>();
 		actions.add(factory.actions().output(OFPort.CONTROLLER, Integer.MAX_VALUE));
 		// done
 		FlowMod3 = FlowMod3.createBuilder().setMatch(match)
 				.setActions(actions)
-		        .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+				.setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
 				.setBufferId(OFBufferId.NO_BUFFER)
 				.setOutPort(OFPort.ANY)
 				.setPriority(Integer.MAX_VALUE)
@@ -179,14 +185,21 @@ public class StaticFlowTests extends FloodlightTestCase {
 
 
 	private void verifyActions(OFFlowMod testFlowMod, OFFlowMod goodFlowMod) {
-		List<OFAction> goodActions = goodFlowMod.getActions();
-		List<OFAction> testActions = testFlowMod.getActions();
-		assertNotNull(goodActions);
-		assertNotNull(testActions);
-		assertEquals(goodActions.size(), testActions.size());
-		// assumes actions are marshalled in same order; should be safe
-		for(int i = 0; i < goodActions.size(); i++) {
-			assertEquals(goodActions.get(i), testActions.get(i));
+		if ((testFlowMod instanceof OFFlowDelete || 
+				testFlowMod instanceof OFFlowDeleteStrict) &&
+				(goodFlowMod instanceof OFFlowDelete || 
+						goodFlowMod instanceof OFFlowDeleteStrict)) {
+			log.warn("Not verifying actions of two flow-delete messages (they're undefined by design)");
+		} else {
+			List<OFAction> goodActions = goodFlowMod.getActions();
+			List<OFAction> testActions = testFlowMod.getActions();
+			assertNotNull(goodActions);
+			assertNotNull(testActions);
+			assertEquals(goodActions.size(), testActions.size());
+			// assumes actions are marshalled in same order; should be safe
+			for(int i = 0; i < goodActions.size(); i++) {
+				assertEquals(goodActions.get(i), testActions.get(i));
+			}
 		}
 	}
 
@@ -195,7 +208,7 @@ public class StaticFlowTests extends FloodlightTestCase {
 	public void setUp() throws Exception {
 		super.setUp();
 		debugCounterService = new MockDebugCounterService();
-		staticFlowEntryPusher = new StaticFlowEntryPusher();
+		pusher = new StaticEntryPusher();
 		switchService = getMockSwitchService();
 		storage = new MemoryStorageSource();
 		dpid = HexString.toLong(TestSwitch1DPID);
@@ -226,20 +239,20 @@ public class StaticFlowTests extends FloodlightTestCase {
 		restApi.init(fmc);
 		debugCounterService.init(fmc);
 		storage.init(fmc);
-		staticFlowEntryPusher.init(fmc);
+		pusher.init(fmc);
 		debugCounterService.init(fmc);
 		storage.startUp(fmc);
 
 		createStorageWithFlowEntries();
 
-		staticFlowEntryPusher.startUp(fmc);    // again, to hack unittest
+		pusher.startUp(fmc);    // again, to hack unittest
 	}
 
 	@Test
 	public void testStaticFlowPush() throws Exception {
 
 		// verify that flowpusher read all three entries from storage
-		assertEquals(TotalTestRules, staticFlowEntryPusher.countEntries());
+		assertEquals(TotalTestRules, pusher.countEntries());
 
 		// if someone calls mockSwitch.getOutputStream(), return mockOutStream instead
 		//expect(mockSwitch.getOutputStream()).andReturn(mockOutStream).anyTimes();
@@ -253,7 +266,7 @@ public class StaticFlowTests extends FloodlightTestCase {
 		replay(mockSwitch);
 
 		// hook the static pusher up to the fake switch
-		staticFlowEntryPusher.switchAdded(DatapathId.of(dpid));
+		pusher.switchAdded(DatapathId.of(dpid));
 
 		verify(mockSwitch);
 
@@ -274,10 +287,10 @@ public class StaticFlowTests extends FloodlightTestCase {
 
 		// delete two rules and verify they've been removed
 		// this should invoke staticFlowPusher.rowsDeleted()
-		storage.deleteRow(StaticFlowEntryPusher.TABLE_NAME, "TestRule1");
-		storage.deleteRow(StaticFlowEntryPusher.TABLE_NAME, "TestRule2");
+		storage.deleteRow(StaticEntryPusher.TABLE_NAME, "TestRule1");
+		storage.deleteRow(StaticEntryPusher.TABLE_NAME, "TestRule2");
 
-		assertEquals(1, staticFlowEntryPusher.countEntries());
+		assertEquals(1, pusher.countEntries());
 		assertEquals(2, writeCapture.getValues().size());
 
 		OFFlowMod firstDelete = (OFFlowMod) writeCapture.getValues().get(0);
@@ -292,8 +305,8 @@ public class StaticFlowTests extends FloodlightTestCase {
 		writeCapture.reset();
 		FlowMod2 = FlowModUtils.toFlowAdd(FlowMod2);
 		FlowMod2 = FlowMod2.createBuilder().setXid(12).build();
-		storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule2);
-		assertEquals(2, staticFlowEntryPusher.countEntries());
+		storage.insertRow(StaticEntryPusher.TABLE_NAME, TestRule2);
+		assertEquals(2, pusher.countEntries());
 		assertEquals(1, writeCaptureList.getValues().size());
 		List<OFMessage> outList =
 				writeCaptureList.getValues().get(0);
@@ -304,9 +317,9 @@ public class StaticFlowTests extends FloodlightTestCase {
 		writeCaptureList.reset();
 
 		// now try an overwriting update, calling staticFlowPusher.rowUpdated()
-		TestRule3.put(COLUMN_DL_VLAN, 333);
-		storage.updateRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3);
-		assertEquals(2, staticFlowEntryPusher.countEntries());
+		TestRule3.put(StaticEntryPusher.matchFieldToColumnName(MatchFields.VLAN_VID), 333);
+		storage.updateRow(StaticEntryPusher.TABLE_NAME, TestRule3);
+		assertEquals(2, pusher.countEntries());
 		assertEquals(1, writeCaptureList.getValues().size());
 
 		outList = writeCaptureList.getValues().get(0);
@@ -321,9 +334,9 @@ public class StaticFlowTests extends FloodlightTestCase {
 		writeCaptureList.reset();
 
 		// now try an action modifying update, calling staticFlowPusher.rowUpdated()
-		TestRule3.put(COLUMN_ACTIONS, "output=controller,pop_vlan"); // added pop-vlan
-		storage.updateRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3);
-		assertEquals(2, staticFlowEntryPusher.countEntries());
+		TestRule3.put(StaticEntryPusher.Columns.COLUMN_ACTIONS, "output=controller,pop_vlan"); // added pop-vlan
+		storage.updateRow(StaticEntryPusher.TABLE_NAME, TestRule3);
+		assertEquals(2, pusher.countEntries());
 		assertEquals(1, writeCaptureList.getValues().size());
 
 		outList = writeCaptureList.getValues().get(0);
@@ -343,13 +356,13 @@ public class StaticFlowTests extends FloodlightTestCase {
 
 	IStorageSourceService populateStorageWithFlowEntries() {
 		Set<String> indexedColumns = new HashSet<String>();
-		indexedColumns.add(COLUMN_NAME);
-		storage.createTable(StaticFlowEntryPusher.TABLE_NAME, indexedColumns);
-		storage.setTablePrimaryKeyName(StaticFlowEntryPusher.TABLE_NAME, COLUMN_NAME);
+		indexedColumns.add(Columns.COLUMN_NAME);
+		storage.createTable(StaticEntryPusher.TABLE_NAME, indexedColumns);
+		storage.setTablePrimaryKeyName(StaticEntryPusher.TABLE_NAME, Columns.COLUMN_NAME);
 
-		storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule1);
-		storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule2);
-		storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3);
+		storage.insertRow(StaticEntryPusher.TABLE_NAME, TestRule1);
+		storage.insertRow(StaticEntryPusher.TABLE_NAME, TestRule2);
+		storage.insertRow(StaticEntryPusher.TABLE_NAME, TestRule3);
 
 		return storage;
 	}
@@ -357,25 +370,25 @@ public class StaticFlowTests extends FloodlightTestCase {
 	@Test
 	public void testHARoleChanged() throws IOException {
 
-		assert(staticFlowEntryPusher.entry2dpid.containsValue(TestSwitch1DPID));
-		assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1));
-		assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2));
-		assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3));
+		assert(pusher.entry2dpid.containsValue(TestSwitch1DPID));
+		assert(pusher.entriesFromStorage.containsValue(FlowMod1));
+		assert(pusher.entriesFromStorage.containsValue(FlowMod2));
+		assert(pusher.entriesFromStorage.containsValue(FlowMod3));
 
 		/* FIXME: what's the right behavior here ??
         // Send a notification that we've changed to slave
         mfp.dispatchRoleChanged(Role.SLAVE);
         // Make sure we've removed all our entries
-        assert(staticFlowEntryPusher.entry2dpid.isEmpty());
-        assert(staticFlowEntryPusher.entriesFromStorage.isEmpty());
+        assert(StaticEntryPusher.entry2dpid.isEmpty());
+        assert(StaticEntryPusher.entriesFromStorage.isEmpty());
 
         // Send a notification that we've changed to master
         mfp.dispatchRoleChanged(Role.MASTER);
         // Make sure we've learned the entries
-        assert(staticFlowEntryPusher.entry2dpid.containsValue(TestSwitch1DPID));
-        assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1));
-        assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2));
-        assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3));
+        assert(StaticEntryPusher.entry2dpid.containsValue(TestSwitch1DPID));
+        assert(StaticEntryPusher.entriesFromStorage.containsValue(FlowMod1));
+        assert(StaticEntryPusher.entriesFromStorage.containsValue(FlowMod2));
+        assert(StaticEntryPusher.entriesFromStorage.containsValue(FlowMod3));
 		 */
 	}
 }
diff --git a/src/test/java/net/floodlightcontroller/topology/TopologyInstanceTest.java b/src/test/java/net/floodlightcontroller/topology/TopologyInstanceTest.java
index 7488a8aecef496d8d7ee78adadfcd59cd803ec77..47790482095cfd3e4ff691c13f3f4679e62d307e 100644
--- a/src/test/java/net/floodlightcontroller/topology/TopologyInstanceTest.java
+++ b/src/test/java/net/floodlightcontroller/topology/TopologyInstanceTest.java
@@ -32,8 +32,6 @@ import net.floodlightcontroller.core.test.MockThreadPoolService;
 import net.floodlightcontroller.core.types.NodePortTuple;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
@@ -69,7 +67,6 @@ public class TopologyInstanceTest {
         fmc.addService(IOFSwitchService.class, new MockSwitchManager());
         fmc.addService(ILinkDiscoveryService.class, linkDiscovery);
         fmc.addService(IDebugCounterService.class, new MockDebugCounterService());
-        fmc.addService(IDebugEventService.class, new MockDebugEventService());
         MockThreadPoolService tp = new MockThreadPoolService();
         topologyManager = new TopologyManager();
         fmc.addService(IThreadPoolService.class, tp);
diff --git a/src/test/java/net/floodlightcontroller/topology/TopologyManagerTest.java b/src/test/java/net/floodlightcontroller/topology/TopologyManagerTest.java
index dabfaa196fd8b9ac01feae2ddc225d93b3974a3d..5310f0af93fac8a3bc0ee29500228251c3aeefc7 100644
--- a/src/test/java/net/floodlightcontroller/topology/TopologyManagerTest.java
+++ b/src/test/java/net/floodlightcontroller/topology/TopologyManagerTest.java
@@ -22,8 +22,6 @@ import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.test.MockThreadPoolService;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.linkdiscovery.ILinkDiscovery;
 import net.floodlightcontroller.test.FloodlightTestCase;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
@@ -49,7 +47,6 @@ public class TopologyManagerTest extends FloodlightTestCase {
         fmc = new FloodlightModuleContext();
         fmc.addService(IFloodlightProviderService.class, getMockFloodlightProvider());
         fmc.addService(IDebugCounterService.class, new MockDebugCounterService());
-        fmc.addService(IDebugEventService.class, new MockDebugEventService());
         MockThreadPoolService tp = new MockThreadPoolService();
         fmc.addService(IThreadPoolService.class, tp);
         tm  = new TopologyManager();
diff --git a/src/test/java/net/floodlightcontroller/util/OFMessageDamperTest.java b/src/test/java/net/floodlightcontroller/util/OFMessageDamperTest.java
index a1a36ff7ee924ae7eed7fd1a75b2b34d84bb9cd0..8d6e154fc5735d6f8ca64bc91ff9e80f837cd9f9 100644
--- a/src/test/java/net/floodlightcontroller/util/OFMessageDamperTest.java
+++ b/src/test/java/net/floodlightcontroller/util/OFMessageDamperTest.java
@@ -53,11 +53,11 @@ public class OFMessageDamperTest {
         sw1 = new OFMessageDamperMockSwitch();
         sw2 = new OFMessageDamperMockSwitch();
         
-        echoRequst1 = factory.buildEchoRequest().setData(new byte[] { 1 }).build();
-        echoRequst1Clone = echoRequst1.createBuilder().build();
-        echoRequst2 = factory.buildEchoRequest().setData(new byte[] { 2 }).build();
+        echoRequst1 = factory.buildEchoRequest().setXid(1L).setData(new byte[] { 1 }).build();
+        echoRequst1Clone = echoRequst1.createBuilder().setXid(2L).build(); /* same as above except ++xid */
+        echoRequst2 = factory.buildEchoRequest().setXid(2L).setData(new byte[] { 2 }).build(); /* now change the data */
         
-        hello1 = factory.buildHello().setXid(1L).build();
+        hello1 = factory.buildHello().setXid(1L).build(); /* different XIDs */
         hello2 = factory.buildHello().setXid(2L).build();
     }
     
@@ -136,9 +136,9 @@ public class OFMessageDamperTest {
         doWrite(false, sw1, hello1);
         doWrite(false, sw1, hello1);
         
-        doWrite(true, sw1, hello2);
-        doWrite(false, sw1, hello2);
-        doWrite(false, sw1, hello2);
+        doWrite(true, sw2, hello2);
+        doWrite(false, sw2, hello2);
+        doWrite(false, sw2, hello2);
         
         // echo request should also be dampened on sw2
         doWrite(true, sw2, echoRequst1);
@@ -149,7 +149,7 @@ public class OFMessageDamperTest {
         doWrite(true, sw1, echoRequst1);
         doWrite(true, sw2, echoRequst1);
         doWrite(true, sw1, hello1);
-        doWrite(true, sw1, hello2);
+        doWrite(true, sw2, hello2);
     }
     
 }
diff --git a/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java b/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java
index 3c07e937b3a309833f9e283b69a24441b952522a..7f39f979f2a8fe01a52e917966e30168d93da891 100644
--- a/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java
+++ b/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java
@@ -50,8 +50,6 @@ import net.floodlightcontroller.core.test.MockThreadPoolService;
 import net.floodlightcontroller.core.test.PacketFactory;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.devicemanager.IDeviceService;
 import net.floodlightcontroller.devicemanager.IEntityClassifierService;
 import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier;
@@ -128,7 +126,6 @@ public class VirtualNetworkFilterTest extends FloodlightTestCase {
         fmc.addService(ITopologyService.class, topology);
         fmc.addService(ISyncService.class, mockSyncService);
         fmc.addService(IDebugCounterService.class, new MockDebugCounterService());
-        fmc.addService(IDebugEventService.class, new MockDebugEventService());
         tps.init(fmc);
         deviceService.init(fmc);
         restApi.init(fmc);
diff --git a/src/test/java/org/sdnplatform/sync/client/ClientTest.java b/src/test/java/org/sdnplatform/sync/client/ClientTest.java
index 0ba68ebe6f6eb407277be5dca77def354c5cc2a5..2f67804b7f28e2357a18b88449d1c900b00cf276 100644
--- a/src/test/java/org/sdnplatform/sync/client/ClientTest.java
+++ b/src/test/java/org/sdnplatform/sync/client/ClientTest.java
@@ -10,8 +10,6 @@ import java.util.ArrayList;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
 import net.floodlightcontroller.threadpool.ThreadPool;
 
@@ -64,7 +62,6 @@ public class ClientTest {
         FloodlightModuleContext fmc = new FloodlightModuleContext();
         fmc.addService(IThreadPoolService.class, tp);
         fmc.addService(IDebugCounterService.class, new MockDebugCounterService());
-        fmc.addService(IDebugEventService.class, new MockDebugEventService());
         
         fmc.addConfigParam(syncManager, "nodes", nodeString);
         fmc.addConfigParam(syncManager, "thisNodeId", ""+1);
diff --git a/src/test/java/org/sdnplatform/sync/internal/BootstrapTest.java b/src/test/java/org/sdnplatform/sync/internal/BootstrapTest.java
index 72e3a5bf73f0bd838c378b4c8fd8a400d7109c23..c21441587255652f7a009a954bc48d4713a1d2e9 100644
--- a/src/test/java/org/sdnplatform/sync/internal/BootstrapTest.java
+++ b/src/test/java/org/sdnplatform/sync/internal/BootstrapTest.java
@@ -6,8 +6,6 @@ import java.util.ArrayList;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
 import net.floodlightcontroller.threadpool.ThreadPool;
 
@@ -64,7 +62,6 @@ public class BootstrapTest {
 
             fmc.addService(IThreadPoolService.class, tp);
             fmc.addService(IDebugCounterService.class, new MockDebugCounterService());
-            fmc.addService(IDebugEventService.class, new MockDebugEventService());
             String dbPath = 
                     new File(dbFolder.getRoot(), 
                              "server" + i).getAbsolutePath();
diff --git a/src/test/java/org/sdnplatform/sync/internal/SyncManagerTest.java b/src/test/java/org/sdnplatform/sync/internal/SyncManagerTest.java
index 418fbfdf321957808e3e732da5973cfa6d4067c5..1a38875883b6d4da2cd5e9ba732d2bf4d42833fc 100644
--- a/src/test/java/org/sdnplatform/sync/internal/SyncManagerTest.java
+++ b/src/test/java/org/sdnplatform/sync/internal/SyncManagerTest.java
@@ -15,8 +15,6 @@ import java.util.Map.Entry;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
 import net.floodlightcontroller.threadpool.ThreadPool;
 
@@ -74,7 +72,6 @@ public class SyncManagerTest {
             throws Exception {        
         fmc.addService(IThreadPoolService.class, tp);
         fmc.addService(IDebugCounterService.class, new MockDebugCounterService());
-        fmc.addService(IDebugEventService.class, new MockDebugEventService());
         fmc.addConfigParam(syncManager, "configProviders", 
                            PropertyCCProvider.class.getName());
         fmc.addConfigParam(syncManager, "nodes", nodeString);
diff --git a/src/test/java/org/sdnplatform/sync/internal/store/RemoteStoreTest.java b/src/test/java/org/sdnplatform/sync/internal/store/RemoteStoreTest.java
index 9ee89bae40420703d413cde266ce16c01c4db0c9..db02a81bd8b6cde73e995aac42845e3fe262692b 100644
--- a/src/test/java/org/sdnplatform/sync/internal/store/RemoteStoreTest.java
+++ b/src/test/java/org/sdnplatform/sync/internal/store/RemoteStoreTest.java
@@ -6,8 +6,6 @@ import java.util.List;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.MockDebugCounterService;
-import net.floodlightcontroller.debugevent.IDebugEventService;
-import net.floodlightcontroller.debugevent.MockDebugEventService;
 import net.floodlightcontroller.threadpool.IThreadPoolService;
 import net.floodlightcontroller.threadpool.ThreadPool;
 
@@ -39,7 +37,6 @@ public class RemoteStoreTest extends AbstractStoreT<ByteArray,byte[]> {
 
         fmc.addService(IThreadPoolService.class, tp);
         fmc.addService(IDebugCounterService.class, new MockDebugCounterService());
-        fmc.addService(IDebugEventService.class, new MockDebugEventService());
         fmc.addConfigParam(syncManager, "persistenceEnabled", "false");
         
         tp.init(fmc);
diff --git a/src/test/java/org/sdnplatform/sync/test/MockSyncService.java b/src/test/java/org/sdnplatform/sync/test/MockSyncService.java
index 325892b772fe75dfb8c2aa3aa4bbedfd20430070..6140d0f876c023d62e518d7788f8f634819788a4 100644
--- a/src/test/java/org/sdnplatform/sync/test/MockSyncService.java
+++ b/src/test/java/org/sdnplatform/sync/test/MockSyncService.java
@@ -7,7 +7,6 @@ import org.sdnplatform.sync.ISyncService;
 import org.sdnplatform.sync.error.SyncException;
 import org.sdnplatform.sync.error.UnknownStoreException;
 import org.sdnplatform.sync.internal.AbstractSyncManager;
-import org.sdnplatform.sync.internal.config.ClusterConfig;
 import org.sdnplatform.sync.internal.rpc.IRPCListener;
 import org.sdnplatform.sync.internal.store.IStorageEngine;
 import org.sdnplatform.sync.internal.store.IStore;