diff --git a/build.xml b/build.xml
index a430867363dfacede2ee55be60f7af9389a72113..3ef76bce1ac4da9f4a0ae443a906b44c5ad7e191 100644
--- a/build.xml
+++ b/build.xml
@@ -69,7 +69,7 @@
         <include name="org.restlet.ext.jsslutils.jar"/>
         <include name="org.simpleframework.jar"/>
         <include name="org.jsslutils.jar"/>
-        <include name="netty-3.10.0.Final.jar"/>
+        <include name="netty-all-4.0.31.Final.jar"/>
         <include name="args4j-2.0.16.jar"/>
         <include name="concurrentlinkedhashmap-lru-1.2.jar"/>
         <include name="jython-2.5.2.jar"/>
@@ -78,8 +78,8 @@
         <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-0.9.0-SNAPSHOT.jar"/>
-    	<include name="openflowj-0.9.0-SNAPSHOT-javadoc.jar"/>
+    	<include name="openflowj-2.0.0-SNAPSHOT.jar"/>
+    	<include name="openflowj-2.0.0-SNAPSHOT-javadoc.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/netty-3.10.0.Final.jar b/lib/netty-3.10.0.Final.jar
deleted file mode 100644
index 5f8aa2e2eb08d43a1a8c90c13bafcec5fdd11d63..0000000000000000000000000000000000000000
Binary files a/lib/netty-3.10.0.Final.jar and /dev/null differ
diff --git a/lib/netty-all-4.0.31.Final.jar b/lib/netty-all-4.0.31.Final.jar
new file mode 100644
index 0000000000000000000000000000000000000000..ef9477a323af6de76bd1a0b4cda9c4da0e72860d
Binary files /dev/null and b/lib/netty-all-4.0.31.Final.jar differ
diff --git a/lib/openflowj-0.9.0-SNAPSHOT-sources.jar b/lib/openflowj-0.9.0-SNAPSHOT-sources.jar
deleted file mode 100644
index abdf436f91750e82a1e50f3376105bf590526477..0000000000000000000000000000000000000000
Binary files a/lib/openflowj-0.9.0-SNAPSHOT-sources.jar and /dev/null differ
diff --git a/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar b/lib/openflowj-2.0.0-SNAPSHOT-javadoc.jar
similarity index 53%
rename from lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar
rename to lib/openflowj-2.0.0-SNAPSHOT-javadoc.jar
index 3b2cd87c8da1a2ba622894c2241b4321b59e6ce2..25f13543563de36b77d132b3c5434728fc7395bd 100644
Binary files a/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar and b/lib/openflowj-2.0.0-SNAPSHOT-javadoc.jar differ
diff --git a/lib/openflowj-2.0.0-SNAPSHOT-sources.jar b/lib/openflowj-2.0.0-SNAPSHOT-sources.jar
new file mode 100644
index 0000000000000000000000000000000000000000..f2403b8ef0d5ab3f73ba02ffa09690909da8d786
Binary files /dev/null and b/lib/openflowj-2.0.0-SNAPSHOT-sources.jar differ
diff --git a/lib/openflowj-0.9.0-SNAPSHOT.jar b/lib/openflowj-2.0.0-SNAPSHOT.jar
similarity index 63%
rename from lib/openflowj-0.9.0-SNAPSHOT.jar
rename to lib/openflowj-2.0.0-SNAPSHOT.jar
index 2a2140a0038065ee1bc875723a42ae9b9cf8ece0..7f25d80d801bfca393c2ea807f9df8349c418fac 100644
Binary files a/lib/openflowj-0.9.0-SNAPSHOT.jar and b/lib/openflowj-2.0.0-SNAPSHOT.jar differ
diff --git a/logback.xml b/logback.xml
index f5163007d4c2af4ea21c0c62b023614e2689ee79..0485717cddefe39bd795685712cce3e4c0af18ad 100644
--- a/logback.xml
+++ b/logback.xml
@@ -7,9 +7,8 @@
   <root level="DEBUG">
     <appender-ref ref="STDOUT" />
   </root>
-  <logger name="org" level="DEBUG"/>
+  <logger name="io" level="INFO"></logger> <!-- Netty logging -->
   <logger name="LogService" level="DEBUG"/> <!-- Restlet access logging -->
-  <logger name="net.floodlightcontroller" level="DEBUG"/>
-  <logger name="net.floodlightcontroller.logging" level="DEBUG"/>
+  <logger name="net.floodlightcontroller" level="INFO"/>
   <logger name="org.sdnplatform" level="INFO"/>
 </configuration>
diff --git a/pom.xml b/pom.xml
index 05c0b1784d45c95844c78a6f0f48256f030475db..d10617a645d8a137a9204b081e002a3b41660298 100644
--- a/pom.xml
+++ b/pom.xml
@@ -212,12 +212,12 @@
 		<dependency>
 			<groupId>org.projectfloodlight</groupId>
 			<artifactId>openflowj</artifactId>
-			<version>0.9.0-SNAPSHOT</version>
+			<version>2.0.0-SNAPSHOT</version>
 		</dependency>
 		<dependency>
 			<groupId>io.netty</groupId>
 			<artifactId>netty</artifactId>
-			<version>3.9.0.Final</version>
+			<version>4.0.31.Final</version>
 		</dependency>
 		<dependency>
 			<groupId>com.fasterxml.jackson.dataformat</groupId>
diff --git a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
index d676bfe207b4cf179675be641d4add2cc7761c9b..74043b84a824eff5683a6fb924a6da2010631dc5 100644
--- a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
+++ b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
@@ -23,9 +23,7 @@ import java.util.Map;
 
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.packet.Ethernet;
-
-import org.jboss.netty.util.Timer;
-
+import io.netty.util.Timer;
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.HARole;
 import net.floodlightcontroller.core.IHAListener;
@@ -36,10 +34,12 @@ import net.floodlightcontroller.core.RoleInfo;
 import net.floodlightcontroller.core.internal.RoleManager;
 import net.floodlightcontroller.core.internal.Controller.IUpdate;
 import net.floodlightcontroller.core.internal.Controller.ModuleLoaderState;
-
 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.
@@ -115,16 +115,16 @@ public interface IFloodlightProviderService extends
     public String getControllerId();
 
     /**
-     * Gets the controller hostname
-     * @return the controller hostname
+     * Gets the controller addresses
+     * @return the controller addresses
      */
-    public String getOFHostname();
+    public Set<IPv4Address> getOFAddresses();
 
     /**
      * Gets the controller's openflow port
      * @return the controller's openflow port
      */
-    public int getOFPort();
+    public TransportPort getOFPort();
 
     /**
      * Set the role of the controller
diff --git a/src/main/java/net/floodlightcontroller/core/IOFConnection.java b/src/main/java/net/floodlightcontroller/core/IOFConnection.java
index 21855fd33aeb96714451ae38d5dee1b88dc16383..2537d40a7984c6843ed8b4b206e4cdce53034711 100644
--- a/src/main/java/net/floodlightcontroller/core/IOFConnection.java
+++ b/src/main/java/net/floodlightcontroller/core/IOFConnection.java
@@ -22,12 +22,6 @@ public interface IOFConnection extends IOFMessageWriter {
      */
     Date getConnectedSince();
 
-    /**
-     * Flush all flows queued for this switch in the current thread.
-     * NOTE: The contract is limited to the current thread
-     */
-    void flush();
-
     /** @return the DatapathId of the switch associated with the connection */
     DatapathId getDatapathId();
 
diff --git a/src/main/java/net/floodlightcontroller/core/IOFMessageWriter.java b/src/main/java/net/floodlightcontroller/core/IOFMessageWriter.java
index bd27181f19e9170fac71db1557942dcc8cef0510..a8a7e3191dda047e57a143f959be27cb5ba5dbae 100644
--- a/src/main/java/net/floodlightcontroller/core/IOFMessageWriter.java
+++ b/src/main/java/net/floodlightcontroller/core/IOFMessageWriter.java
@@ -17,6 +17,7 @@
 
 package net.floodlightcontroller.core;
 
+import java.util.Collection;
 import java.util.List;
 
 import org.projectfloodlight.openflow.protocol.OFMessage;
@@ -33,25 +34,24 @@ import com.google.common.util.concurrent.ListenableFuture;
 
 public interface IOFMessageWriter{
 
-    /**
-     * Writes to the OFMessage to the output stream.
-     *
-     * <p><b>Note:</b> this method has fire-and-forget semantics. When the connection is
-     * not currently connected, it will silently discard the messages.
+	/**
+     * Writes the OFMessage to the output stream.
      *
      * @param m
+     * @return true upon success; false if message could not be written
      */
-    void write(OFMessage m);
+    boolean write(OFMessage m);
 
     /**
      * Writes the list of messages to the output stream.
      *
-     * <p><b>Note:</b> this method has fire-and-forget semantics. When the connection is
-     * not currently connected, it will silently discard the messages.
+     * Any messages that could not be written due to channel disconnect
+     * will be returned.
      *
      * @param msglist
+     * @return list of messages that could not be written
      */
-    void write(Iterable<OFMessage> msglist);
+    Collection<OFMessage> write(Iterable<OFMessage> msgList);
     
     /** write an OpenFlow Request message, register for a single corresponding reply message
      *  or error message.
diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java
index 4cc47498d8534a5cce3f24e78243f4cac7313394..b6a96f31ffc161d5443ee066acaefd9ffc136bdb 100644
--- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java
+++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java
@@ -40,6 +40,7 @@ import org.projectfloodlight.openflow.types.U64;
 import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.ListenableFuture;
 
+import net.floodlightcontroller.core.internal.OFConnection;
 import net.floodlightcontroller.core.internal.TableFeatures;
 import net.floodlightcontroller.core.web.serializers.IOFSwitchSerializer;
 
@@ -289,13 +290,6 @@ public interface IOFSwitch extends IOFMessageWriter {
      */
     OFFactory getOFFactory();
 
-    /**
-     * Flush all flows queued for this switch on all connections that were written by the current thread.
-     *
-     *
-     */
-    void flush();
-
     /**
      * Gets the OF connections for this switch instance
      * @return Collection of IOFConnection
@@ -306,15 +300,17 @@ public interface IOFSwitch extends IOFMessageWriter {
      * Writes a message to the connection specified by the logical OFMessage category
      * @param m an OF Message
      * @param category the category of the OF Message to be sent
+     * @return true upon success; false upon failure
      */
-    void write(OFMessage m, LogicalOFMessageCategory category);
+    boolean write(OFMessage m, LogicalOFMessageCategory category);
 
     /**
      * Writes a message list to the connection specified by the logical OFMessage category
      * @param msglist an OF Message list
      * @param category the category of the OF Message list to be sent
+     * @return list of failed messages, if any; success denoted by empty list
      */
-    void write(Iterable<OFMessage> msglist, LogicalOFMessageCategory category);
+    Iterable<OFMessage> write(Iterable<OFMessage> msglist, LogicalOFMessageCategory category);
 
     /**
      * Get a connection specified by the logical OFMessage category
diff --git a/src/main/java/net/floodlightcontroller/core/OFConnection.java b/src/main/java/net/floodlightcontroller/core/OFConnection.java
deleted file mode 100644
index c72c618c6d2073cd1738baa0d35c7a3c95a3b5e6..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/core/OFConnection.java
+++ /dev/null
@@ -1,430 +0,0 @@
-/**
- *    Copyright 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.
- **/
-
-package net.floodlightcontroller.core;
-
-import java.net.SocketAddress;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.annotation.Nonnull;
-
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.util.Timeout;
-import org.jboss.netty.util.Timer;
-import org.jboss.netty.util.TimerTask;
-
-import java.util.Date;
-
-import net.floodlightcontroller.core.internal.Controller;
-import net.floodlightcontroller.core.internal.IOFConnectionListener;
-import net.floodlightcontroller.debugcounter.IDebugCounterService;
-
-import org.projectfloodlight.openflow.protocol.OFErrorMsg;
-import org.projectfloodlight.openflow.protocol.OFFactory;
-import org.projectfloodlight.openflow.protocol.OFMessage;
-import org.projectfloodlight.openflow.protocol.OFRequest;
-import org.projectfloodlight.openflow.protocol.OFStatsReply;
-import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
-import org.projectfloodlight.openflow.protocol.OFStatsRequest;
-import org.projectfloodlight.openflow.protocol.OFType;
-import org.projectfloodlight.openflow.types.DatapathId;
-import org.projectfloodlight.openflow.types.OFAuxId;
-import org.projectfloodlight.openflow.types.U64;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * Implementation of an openflow connection to switch. Encapsulates a
- * {@link Channel}, and provides message write and request/response handling
- * capabilities.
- *
- * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
- */
-public class OFConnection implements IOFConnection, IOFConnectionBackend{
-    private static final Logger logger = LoggerFactory.getLogger(OFConnection.class);
-    private final DatapathId dpid;
-    private final OFFactory factory;
-    private final Channel channel;
-    private final OFAuxId auxId;
-    private final Timer timer;
-
-    private final Date connectedSince;
-
-    private final Map<Long, Deliverable<?>> xidDeliverableMap;
-
-    protected final static ThreadLocal<List<OFMessage>> localMsgBuffer =
-            new ThreadLocal<List<OFMessage>>();
-
-    private static final long DELIVERABLE_TIME_OUT = 60;
-    private static final TimeUnit DELIVERABLE_TIME_OUT_UNIT = TimeUnit.SECONDS;
-
-    private final OFConnectionCounters counters;
-    private IOFConnectionListener listener;
-    
-    private volatile U64 latency;
-
-    public OFConnection(@Nonnull DatapathId dpid,
-                        @Nonnull OFFactory factory,
-                        @Nonnull Channel channel,
-                        @Nonnull OFAuxId auxId,
-                        @Nonnull IDebugCounterService debugCounters,
-                        @Nonnull Timer timer) {
-        Preconditions.checkNotNull(dpid, "dpid");
-        Preconditions.checkNotNull(factory, "factory");
-        Preconditions.checkNotNull(channel, "channel");
-        Preconditions.checkNotNull(timer, "timer");
-        Preconditions.checkNotNull(debugCounters);
-
-        this.listener = NullConnectionListener.INSTANCE;
-        this.dpid = dpid;
-        this.factory = factory;
-        this.channel = channel;
-        this.auxId = auxId;
-        this.connectedSince = new Date();
-        this.xidDeliverableMap = new ConcurrentHashMap<>();
-        this.counters = new OFConnectionCounters(debugCounters, dpid, this.auxId);
-        this.timer = timer;
-        this.latency = U64.ZERO;
-    }
-
-    @Override
-    public void write(OFMessage m) {
-        if (!isConnected()) {
-            if (logger.isDebugEnabled())
-                logger.debug("{}: not connected - dropping message {}", this, m);
-            return;
-        }
-        if (logger.isDebugEnabled())
-            logger.debug("{}: send {}", this, m);
-        List<OFMessage> msgBuffer = localMsgBuffer.get();
-        if (msgBuffer == null) {
-            msgBuffer = new ArrayList<OFMessage>();
-            localMsgBuffer.set(msgBuffer);
-        }
-
-        counters.updateWriteStats(m);
-        msgBuffer.add(m);
-
-        if ((msgBuffer.size() >= Controller.BATCH_MAX_SIZE) || ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
-            this.write(msgBuffer);
-            localMsgBuffer.set(null);
-        }
-    }
-
-    @Override
-    public <R extends OFMessage> ListenableFuture<R> writeRequest(OFRequest<R> request) {
-        if (!isConnected())
-            return Futures.immediateFailedFuture(new SwitchDisconnectedException(getDatapathId()));
-
-        DeliverableListenableFuture<R> future = new DeliverableListenableFuture<R>();
-        xidDeliverableMap.put(request.getXid(), future);
-        listener.messageWritten(this, request);
-        write(request);
-        return future;
-    }
-
-    @Override
-    public void write(Iterable<OFMessage> msglist) {
-        if (!isConnected()) {
-            if (logger.isDebugEnabled())
-                logger.debug(this.toString() + " : not connected - dropping {} element msglist {} ",
-                        Iterables.size(msglist),
-                        String.valueOf(msglist).substring(0, 80));
-            return;
-        }
-        for (OFMessage m : msglist) {
-            if (logger.isTraceEnabled())
-                logger.trace("{}: send {}", this, m);
-            counters.updateWriteStats(m);
-        }
-        this.channel.write(msglist);
-    }
-
-    // Notifies the connection object that the channel has been disconnected
-    public void disconnected() {
-        SwitchDisconnectedException exception = new SwitchDisconnectedException(getDatapathId());
-        for (Long xid : xidDeliverableMap.keySet()) {
-            // protect against other mechanisms running at the same time
-            // (timeout)
-            Deliverable<?> removed = xidDeliverableMap.remove(xid);
-            if (removed != null) {
-                removed.deliverError(exception);
-            }
-        }
-    }
-
-    @Override
-    public void disconnect() {
-        this.channel.disconnect();
-        this.counters.uninstallCounters();
-    }
-
-    @Override
-    public String toString() {
-        String channelString = (channel != null) ? String.valueOf(channel.getRemoteAddress()): "?";
-        return "OFConnection [" + getDatapathId() + "(" + getAuxId() + ")" + "@" + channelString + "]";
-    }
-
-    @Override
-    public Date getConnectedSince() {
-        return connectedSince;
-    }
-
-    @Override
-    public <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> writeStatsRequest(
-            OFStatsRequest<REPLY> request) {
-        if (!isConnected())
-            return Futures.immediateFailedFuture(new SwitchDisconnectedException(getDatapathId()));
-
-        final DeliverableListenableFuture<List<REPLY>> future =
-                new DeliverableListenableFuture<List<REPLY>>();
-
-        Deliverable<REPLY> deliverable = new Deliverable<REPLY>() {
-            private final List<REPLY> results = Collections
-                    .synchronizedList(new ArrayList<REPLY>());
-
-            @Override
-            public void deliver(REPLY reply) {
-                results.add(reply);
-                if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
-                    // done
-                    future.deliver(results);
-                }
-            }
-
-            @Override
-            public void deliverError(Throwable cause) {
-                future.deliverError(cause);
-            }
-
-            @Override
-            public boolean isDone() {
-                return future.isDone();
-            }
-
-            @Override
-            public boolean cancel(boolean mayInterruptIfRunning) {
-                return future.cancel(mayInterruptIfRunning);
-            }
-        };
-
-        registerDeliverable(request.getXid(), deliverable);
-        this.write(request);
-        return future;
-    }
-
-    private void registerDeliverable(long xid, Deliverable<?> deliverable) {
-        this.xidDeliverableMap.put(xid, deliverable);
-        timer.newTimeout(new TimeOutDeliverable(xid), DELIVERABLE_TIME_OUT, DELIVERABLE_TIME_OUT_UNIT);
-    }
-
-    public boolean handleGenericDeliverable(OFMessage reply) {
-        counters.updateReadStats(reply);
-        @SuppressWarnings("unchecked")
-        Deliverable<OFMessage> deliverable =
-                (Deliverable<OFMessage>) this.xidDeliverableMap.get(reply.getXid());
-        if (deliverable != null) {
-            if(reply instanceof OFErrorMsg) {
-                deliverable.deliverError(new OFErrorMsgException((OFErrorMsg) reply));
-            } else {
-                deliverable.deliver(reply);
-            }
-            if (deliverable.isDone())
-                this.xidDeliverableMap.remove(reply.getXid());
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    public void cancelAllPendingRequests() {
-        /*
-         * we don't need to be synchronized here. Even if another thread
-         * modifies the map while we're cleaning up the future will eventually
-         * timeout
-         */
-        for (Deliverable<?> d : xidDeliverableMap.values()) {
-            d.cancel(true);
-        }
-        xidDeliverableMap.clear();
-    }
-
-    @Override
-    public boolean isConnected() {
-        return channel.isConnected();
-    }
-
-    @Override
-    public void flush() {
-        List<OFMessage> msglist = localMsgBuffer.get();
-        if ((msglist != null) && (msglist.size() > 0)) {
-            this.write(msglist);
-            localMsgBuffer.set(null);
-        }
-    }
-
-    @Override
-    public SocketAddress getRemoteInetAddress() {
-        return channel.getRemoteAddress();
-    }
-
-    @Override
-    public SocketAddress getLocalInetAddress() {
-        return channel.getLocalAddress();
-    }
-
-    public boolean deliverResponse(OFMessage m) {
-        if (handleGenericDeliverable(m))
-            return true;
-        else
-            return false;
-    }
-
-    @Override
-    public boolean isWritable() {
-        return channel.isWritable();
-    }
-
-    @Override
-    public DatapathId getDatapathId() {
-        return dpid;
-    }
-
-    @Override
-    public OFAuxId getAuxId() {
-        return auxId;
-    }
-
-    Set<Long> getPendingRequestIds() {
-        return ImmutableSet.copyOf(xidDeliverableMap.keySet());
-    }
-
-    @Override
-    public OFFactory getOFFactory() {
-        return this.factory;
-    }
-
-    /**
-     * Timeout class instantiated for deliverables. Will throw a timeout exception
-     * if proper responses are not received in time.
-     *
-     */
-    private class TimeOutDeliverable implements TimerTask {
-        private final long xid;
-
-        public TimeOutDeliverable(long xid) {
-            this.xid = xid;
-        }
-
-        @Override
-        public void run(Timeout timeout) throws Exception {
-            Deliverable<?> removed = xidDeliverableMap.remove(xid);
-            if (removed != null && !removed.isDone()) {
-                removed.deliverError(new TimeoutException(
-                        "timeout - did not receive answer for xid " + xid));
-            }
-
-        }
-    }
-
-    public IOFConnectionListener getListener() {
-        return listener;
-    }
-
-    /** set the connection listener
-     *  <p>
-     *  Note: this is assumed to be called from the Connection's IO Thread.
-     *
-     * @param listener
-     */
-    @Override
-    public void setListener(IOFConnectionListener listener) {
-        this.listener = listener;
-    }
-
-    public void messageReceived(OFMessage m) {
-        // Check if message was a response for a xid waiting at the switch
-        if(!deliverResponse(m)){
-            listener.messageReceived(this, m);
-        }
-    }
-    
-    @Override
-    public U64 getLatency() {
-    	return this.latency;
-    }
-    
-    @Override
-    public void updateLatency(U64 latency) {
-    	if (latency == null) {
-			logger.error("Latency must be non-null. Ignoring null latency value.");
-			return;
-		} else if (this.latency.equals(U64.ZERO)) { 
-			logger.debug("Recording previously 0ms switch {} latency as {}ms", this.getDatapathId(), latency.getValue());
-			this.latency = latency;
-			return;
-		} else {
-			double oldWeight = 0.30;
-			this.latency = U64.of((long) (this.latency.getValue() * oldWeight + latency.getValue() * (1 - oldWeight)));
-			logger.debug("Switch {} latency updated to {}ms", this.getDatapathId(), this.latency.getValue());
-		}
-    }
-
-    /** A dummy connection listener that just logs warn messages. Saves us a few null checks
-     * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
-     */
-    private static class NullConnectionListener implements IOFConnectionListener {
-        public final static NullConnectionListener INSTANCE = new NullConnectionListener();
-
-        private NullConnectionListener() { }
-
-        @Override
-        public void connectionClosed(IOFConnectionBackend connection) {
-            logger.warn("NullConnectionListener for {} - received connectionClosed", connection);
-        }
-
-        @Override
-        public void messageReceived(IOFConnectionBackend connection, OFMessage m) {
-            logger.warn("NullConnectionListener for {} - received messageReceived: {}", connection, m);
-        }
-
-        @Override
-        public boolean isSwitchHandshakeComplete(IOFConnectionBackend connection) {
-            return false;
-        }
-
-		@Override
-		public void messageWritten(IOFConnectionBackend connection, OFMessage m) {
-			// TODO Auto-generated method stub
-			
-		}
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index f1a3743c81b1371c953a5aee733f38f8e75721ca..02ab55c44359f36a2faf23d13ad6a3eafb940593 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -22,6 +22,7 @@ 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;
@@ -35,9 +36,8 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.LinkedBlockingQueue;
 
-import org.jboss.netty.util.HashedWheelTimer;
-import org.jboss.netty.util.Timer;
-
+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;
@@ -68,7 +68,8 @@ 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.python.modules.synchronize;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.TransportPort;
 
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.perfmon.IPktInProcessingTimeService;
@@ -135,9 +136,10 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
     private IShutdownService shutdownService;
 
     // Configuration options
-    protected int openFlowPort = 6653; // new registered OF port number
-    private String openFlowHostname = null;
-    protected int workerThreads = 0;
+    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.
@@ -147,8 +149,7 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
     // if they should operate in ACTIVE / STANDBY
     protected volatile HARole notifiedRole;
 
-    private static final String
-            INITIAL_ROLE_CHANGE_DESCRIPTION = "Controller startup.";
+    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()
@@ -180,10 +181,6 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
             FLOW_COLUMN_ACCESS_PRIORITY,
             FLOW_COLUMN_CORE_PRIORITY
     };
-    
-    // Perf. related configuration
-    protected static final int SEND_BUFFER_SIZE = 128 * 1024;
-    public static final int BATCH_MAX_SIZE = 1; //TODO @Ryan this was 100. Causes packet_out messages to stall until 100 accumulated...
     protected static final boolean ALWAYS_DECODE_ETH = true;
 
     // Set of port name prefixes that will be classified as uplink ports,
@@ -524,11 +521,12 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
     }
     
     @Override
-    public String getOFHostname() {
-        return openFlowHostname;
+    public Set<IPv4Address> getOFAddresses() {
+        return Collections.unmodifiableSet(openFlowAddresses);
     }
+    
     @Override
-    public int getOFPort() {
+    public TransportPort getOFPort() {
         return openFlowPort;
     }
 
@@ -616,7 +614,6 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
         if (m == null)
             throw new NullPointerException("OFMessage must not be null");
 
-        // FIXME floodlight context not supported any more
         FloodlightContext bc = new FloodlightContext();
 
         List<IOFMessageListener> listeners = null;
@@ -706,22 +703,35 @@ public class Controller implements IFloodlightProviderService, IStorageSourceLis
     }
     
     private void setConfigParams(Map<String, String> configParams) throws FloodlightModuleException {
-        String ofPort = configParams.get("openflowPort");
+        String ofPort = configParams.get("openFlowPort");
         if (!Strings.isNullOrEmpty(ofPort)) {
             try {
-                this.openFlowPort = Integer.parseInt(ofPort);
-            } catch (NumberFormatException e) {
-                log.error("invalid openflow port specifier", e);
-                throw new FloodlightModuleException("invalid port specifier in cofig");
+                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.debug("OpenFlow port set to {}", this.openFlowPort);
         }
+        log.info("OpenFlow port set to {}", openFlowPort);
 
-        String threads = configParams.get("workerthreads");
+        String threads = configParams.get("workerThreads");
         if (!Strings.isNullOrEmpty(threads)) {
             this.workerThreads = Integer.parseInt(threads);
         }
-        log.debug("Number of worker threads set to {}", this.workerThreads);
+        log.info("Number of worker threads set to {}", this.workerThreads);
+        
+        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);
+        } else {
+        	openFlowAddresses.add(IPv4Address.NONE);
+        }
     }
 
     /**
diff --git a/src/main/java/net/floodlightcontroller/core/internal/ControllerCounters.java b/src/main/java/net/floodlightcontroller/core/internal/ControllerCounters.java
index de5b99255c8e2968a7d914b50dc7c0115f330d4c..d14e88d78e91a28fbe37e773cf5c684d4bda51f3 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/ControllerCounters.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/ControllerCounters.java
@@ -1,11 +1,9 @@
 package net.floodlightcontroller.core.internal;
 
 import net.floodlightcontroller.core.IOFSwitchBackend;
-import net.floodlightcontroller.core.OFConnectionCounters;
 import net.floodlightcontroller.debugcounter.IDebugCounter;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import net.floodlightcontroller.debugcounter.IDebugCounterService.MetaData;
-
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD",
diff --git a/src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutHandler.java b/src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutHandler.java
index 89ef68109bdc715eb2c16ea3f59f6c2c7b33fc67..2bf6a7c919072e5f6da9fa42cc85b00856b422bb 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/HandshakeTimeoutHandler.java
@@ -19,22 +19,18 @@ package net.floodlightcontroller.core.internal;
 
 import java.util.concurrent.TimeUnit;
 
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
-import org.jboss.netty.util.Timeout;
-import org.jboss.netty.util.Timer;
-import org.jboss.netty.util.TimerTask;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
 
 /**
  * Trigger a timeout if a switch fails to complete handshake soon enough
  */
-public class HandshakeTimeoutHandler
-    extends SimpleChannelUpstreamHandler {
+public class HandshakeTimeoutHandler extends ChannelInboundHandlerAdapter {
 
-    static final HandshakeTimeoutException EXCEPTION =
-            new HandshakeTimeoutException();
+    static final HandshakeTimeoutException EXCEPTION = new HandshakeTimeoutException();
 
     final OFChannelHandler handshakeHandler;
     final Timer timer;
@@ -52,22 +48,20 @@ public class HandshakeTimeoutHandler
     }
 
     @Override
-    public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
-            throws Exception {
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
         if (timeoutNanos > 0) {
-            timeout = timer.newTimeout(new HandshakeTimeoutTask(ctx),
-                                       timeoutNanos, TimeUnit.NANOSECONDS);
+            timeout = timer.newTimeout(new HandshakeTimeoutTask(ctx), timeoutNanos, TimeUnit.NANOSECONDS);
         }
-        ctx.sendUpstream(e);
+        super.channelActive(ctx);
     }
 
     @Override
-    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
-            throws Exception {
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
         if (timeout != null) {
             timeout.cancel();
             timeout = null;
         }
+        super.channelInactive(ctx);
     }
 
     private final class HandshakeTimeoutTask implements TimerTask {
@@ -84,11 +78,11 @@ public class HandshakeTimeoutHandler
                 return;
             }
 
-            if (!ctx.getChannel().isOpen()) {
+            if (!ctx.channel().isOpen()) {
                 return;
             }
             if (!handshakeHandler.isSwitchHandshakeComplete())
-                Channels.fireExceptionCaught(ctx, EXCEPTION);
+                ctx.fireExceptionCaught(EXCEPTION);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/internal/NaiveSwitchDriverRegistry.java b/src/main/java/net/floodlightcontroller/core/internal/NaiveSwitchDriverRegistry.java
index b6bb90358f88b8c2fa38c6c478c2e8d9cbec64ac..04c68118d54f7e844e8e7988489e3277ee66c51f 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/NaiveSwitchDriverRegistry.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/NaiveSwitchDriverRegistry.java
@@ -11,7 +11,6 @@ import javax.annotation.Nonnull;
 import net.floodlightcontroller.core.IOFConnectionBackend;
 import net.floodlightcontroller.core.IOFSwitchBackend;
 import net.floodlightcontroller.core.IOFSwitchDriver;
-import net.floodlightcontroller.core.OFSwitch;
 import net.floodlightcontroller.core.SwitchDescription;
 
 import org.projectfloodlight.openflow.protocol.OFFactory;
diff --git a/src/main/java/net/floodlightcontroller/core/NullConnection.java b/src/main/java/net/floodlightcontroller/core/internal/NullConnection.java
similarity index 84%
rename from src/main/java/net/floodlightcontroller/core/NullConnection.java
rename to src/main/java/net/floodlightcontroller/core/internal/NullConnection.java
index 34f225e1343a1f32953f786a61555cf053438f8d..4854a1341a868771d42914197944a0465288430a 100644
--- a/src/main/java/net/floodlightcontroller/core/NullConnection.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/NullConnection.java
@@ -1,10 +1,14 @@
-package net.floodlightcontroller.core;
+package net.floodlightcontroller.core.internal;
 
 import java.net.SocketAddress;
+import java.util.Collection;
 import java.util.List;
 import java.util.Date;
 
-import net.floodlightcontroller.core.internal.IOFConnectionListener;
+import net.floodlightcontroller.core.IOFConnectionBackend;
+import net.floodlightcontroller.core.IOFMessageWriter;
+import net.floodlightcontroller.core.SwitchDisconnectedException;
+import net.floodlightcontroller.util.IterableUtils;
 
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
@@ -40,13 +44,15 @@ public class NullConnection implements IOFConnectionBackend, IOFMessageWriter {
     }
 
     @Override
-    public void write(OFMessage m) {
+    public boolean write(OFMessage m) {
         warn();
+        return false;
     }
 
     @Override
-    public void write(Iterable<OFMessage> msglist) {
+    public Collection<OFMessage> write(Iterable<OFMessage> msgList) {
         warn();
+        return IterableUtils.toCollection(msgList);
     }
 
     @Override
@@ -75,11 +81,6 @@ public class NullConnection implements IOFConnectionBackend, IOFMessageWriter {
         // noop
     }
 
-    @Override
-    public void flush() {
-        // noop
-    }
-
     @Override
     public <R extends OFMessage> ListenableFuture<R> writeRequest(OFRequest<R> request) {
         return Futures.immediateFailedFuture(new SwitchDisconnectedException(getDatapathId()));
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
index 7d45c1ee862f08dea0111bed7a63c37d94caf15e..0611648a5252cd623f1ade599777b3a9151c0067 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
@@ -10,25 +10,19 @@ import java.util.concurrent.RejectedExecutionException;
 
 import javax.annotation.Nonnull;
 
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.channel.ExceptionEvent;
-import org.jboss.netty.channel.MessageEvent;
-import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
-import org.jboss.netty.handler.timeout.IdleStateEvent;
-import org.jboss.netty.handler.timeout.IdleStateHandler;
-import org.jboss.netty.handler.timeout.ReadTimeoutException;
-import org.jboss.netty.util.Timer;
-
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.handler.timeout.ReadTimeoutException;
+import io.netty.util.AttributeKey;
+import io.netty.util.Timer;
 import net.floodlightcontroller.core.IOFConnectionBackend;
-import net.floodlightcontroller.core.OFConnection;
-import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandler;
-import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandshakeTimeout;
-import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineIdleReadTimeout;
-import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineIdleWriteTimeout;
+import net.floodlightcontroller.core.internal.OFChannelInitializer.PipelineHandler;
+import net.floodlightcontroller.core.internal.OFChannelInitializer.PipelineHandshakeTimeout;
+import net.floodlightcontroller.core.internal.OFChannelInitializer.PipelineIdleReadTimeout;
+import net.floodlightcontroller.core.internal.OFChannelInitializer.PipelineIdleWriteTimeout;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 
 import org.projectfloodlight.openflow.exceptions.OFParseError;
@@ -62,10 +56,12 @@ import com.google.common.base.Preconditions;
  *  messages to the higher orders of control.
  * @author Jason Parraga <Jason.Parraga@Bigswitch.com>
  */
-class OFChannelHandler extends IdleStateAwareChannelHandler {
+class OFChannelHandler extends SimpleChannelInboundHandler<Iterable<OFMessage>> {
 
 	private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
 
+	public static final AttributeKey<OFChannelInfo> ATTR_CHANNEL_INFO = AttributeKey.valueOf("channelInfo");
+
 	private final ChannelPipeline pipeline;
 	private final INewOFConnectionListener newConnectionListener;
 	private final SwitchManagerCounters counters;
@@ -83,10 +79,10 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 	 * We will count down
 	 */
 	private long handshakeTransactionIds = 0x00FFFFFFFFL;
-	
-    private volatile long echoSendTime;
-    private volatile long featuresLatency;
-    
+
+	private volatile long echoSendTime;
+	private volatile long featuresLatency;
+
 	/**
 	 * Default implementation for message handlers in any OFChannelState.
 	 *
@@ -336,11 +332,11 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 				/* Lookup highest, common supported OpenFlow version */
 				commonVersion = computeOFVersionFromBitmap(bitmaps);
 				if (commonVersion == null) {
-					log.error("Could not negotiate common OpenFlow version for {} with greatest version bitmap algorithm.", channel.getRemoteAddress());
+					log.error("Could not negotiate common OpenFlow version for {} with greatest version bitmap algorithm.", channel.remoteAddress());
 					channel.disconnect();
 					return;
 				} else {
-					log.info("Negotiated OpenFlow version of {} for {} with greatest version bitmap algorithm.", commonVersion.toString(), channel.getRemoteAddress());
+					log.info("Negotiated OpenFlow version of {} for {} with greatest version bitmap algorithm.", commonVersion.toString(), channel.remoteAddress());
 					factory = OFFactories.getFactory(commonVersion);
 					OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class);
 					decoder.setVersion(commonVersion);
@@ -348,16 +344,16 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 			}
 			/* If there's not a bitmap present, choose the lower of the two supported versions. */
 			else if (theirVersion.compareTo(factory.getVersion()) < 0) {
-				log.info("Negotiated down to switch OpenFlow version of {} for {} using lesser hello header algorithm.", theirVersion.toString(), channel.getRemoteAddress());
+				log.info("Negotiated down to switch OpenFlow version of {} for {} using lesser hello header algorithm.", theirVersion.toString(), channel.remoteAddress());
 				factory = OFFactories.getFactory(theirVersion);
 				OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class);
 				decoder.setVersion(theirVersion);
 			} /* else The controller's version is < or = the switch's, so keep original controller factory. */
 			else if (theirVersion.equals(factory.getVersion())) {
-				log.info("Negotiated equal OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.getRemoteAddress());
+				log.info("Negotiated equal OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.remoteAddress());
 			}
 			else {
-				log.info("Negotiated down to controller OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.getRemoteAddress());
+				log.info("Negotiated down to controller OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.remoteAddress());
 			}
 
 			setState(new WaitFeaturesReplyState());
@@ -382,7 +378,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 		void processOFFeaturesReply(OFFeaturesReply  m)
 				throws IOException {
 			featuresReply = m;
-			
+
 			featuresLatency = (System.currentTimeMillis() - featuresLatency) / 2;
 
 			// Mark handshake as completed
@@ -409,7 +405,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 			 * we are.
 			 */
 			if (m.getVersion().equals(factory.getVersion())) {
-				log.warn("Ignoring second hello from {} in state {}. Might be a Brocade.", channel.getRemoteAddress(), state.toString());
+				log.warn("Ignoring second hello from {} in state {}. Might be a Brocade.", channel.remoteAddress(), state.toString());
 			} else {
 				super.processOFHello(m); /* Versions don't match as they should; abort */
 			}
@@ -417,7 +413,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 
 		@Override
 		void processOFPortStatus(OFPortStatus m) {
-			log.warn("Ignoring PORT_STATUS message from {} during OpenFlow channel establishment. Ports will be explicitly queried in a later state.", channel.getRemoteAddress());
+			log.warn("Ignoring PORT_STATUS message from {} during OpenFlow channel establishment. Ports will be explicitly queried in a later state.", channel.remoteAddress());
 		}
 
 		@Override
@@ -429,7 +425,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 		@Override
 		void processOFMessage(OFMessage m) throws IOException {
 			if (m.getType().equals(OFType.PACKET_IN)) {
-				log.warn("Ignoring PACKET_IN message from {} during OpenFlow channel establishment.", channel.getRemoteAddress());
+				log.warn("Ignoring PACKET_IN message from {} during OpenFlow channel establishment.", channel.remoteAddress());
 			} else {
 				super.processOFMessage(m);
 			}
@@ -453,11 +449,11 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 			setSwitchHandshakeTimeout();
 
 			// Handle non 1.3 connections
-			if(featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0){
+			if (featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0){
 				connection = new OFConnection(featuresReply.getDatapathId(), factory, channel, OFAuxId.MAIN, debugCounters, timer);
 			}
 			// Handle 1.3 connections
-			else{
+			else {
 				connection = new OFConnection(featuresReply.getDatapathId(), factory, channel, featuresReply.getAuxiliaryId(), debugCounters, timer);
 
 				// If this is an aux connection, we set a longer echo idle time
@@ -465,10 +461,10 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 					setAuxChannelIdle();
 				}
 			}
-			
+
 			connection.updateLatency(U64.of(featuresLatency));
 			echoSendTime = 0;
-			
+
 			// Notify the connection broker
 			notifyConnectionOpened(connection);
 		}
@@ -588,21 +584,18 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 	}
 
 	@Override
-	public void channelConnected(ChannelHandlerContext ctx,
-			ChannelStateEvent e) throws Exception {
+	public void channelActive(ChannelHandlerContext ctx) throws Exception {
 		log.debug("channelConnected on OFChannelHandler {}", String.format("%08x", System.identityHashCode(this)));
 		counters.switchConnected.increment();
-		channel = e.getChannel();
-		log.info("New switch connection from {}",
-				channel.getRemoteAddress());
+		channel = ctx.channel();
+		log.info("New switch connection from {}", channel.remoteAddress());
 		setState(new WaitHelloState());
 	}
 
 	@Override
-	public void channelDisconnected(ChannelHandlerContext ctx,
-			ChannelStateEvent e) throws Exception {
+	public void channelInactive(ChannelHandlerContext ctx) {
 		// Only handle cleanup connection is even known
-		if(this.connection != null){
+		if (this.connection != null) {
 			// Alert the connection object that the channel has been disconnected
 			this.connection.disconnected();
 			// Punt the cleanup to the Switch Manager
@@ -612,19 +605,19 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 	}
 
 	@Override
-	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
+	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
 			throws Exception {
-		if (e.getCause() instanceof ReadTimeoutException) {
+		if (cause instanceof ReadTimeoutException) {
 
 			if (featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0) {
 				log.error("Disconnecting switch {} due to read timeout on main cxn.",
 						getConnectionInfoString());
-				ctx.getChannel().close();
+				ctx.channel().close();
 			} else {
 				if (featuresReply.getAuxiliaryId().equals(OFAuxId.MAIN)) {
 					log.error("Disconnecting switch {} due to read timeout on main cxn.",
 							getConnectionInfoString());
-					ctx.getChannel().close();
+					ctx.channel().close();
 				} else {
 					// We only don't disconnect on aux connections
 					log.warn("Switch {} encountered read timeout on aux cxn.",
@@ -634,95 +627,84 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 			// Increment counters
 			counters.switchDisconnectReadTimeout.increment();
 
-		} else if (e.getCause() instanceof HandshakeTimeoutException) {
+		} else if (cause instanceof HandshakeTimeoutException) {
 			log.error("Disconnecting switch {}: failed to complete handshake. Channel handshake complete : {}",
 					getConnectionInfoString(),
 					this.state.channelHandshakeComplete);
 			counters.switchDisconnectHandshakeTimeout.increment();
-			ctx.getChannel().close();
-		} else if (e.getCause() instanceof ClosedChannelException) {
+			ctx.channel().close();
+		} else if (cause instanceof ClosedChannelException) {
 			log.debug("Channel for sw {} already closed", getConnectionInfoString());
-		} else if (e.getCause() instanceof IOException) {
+		} else if (cause instanceof IOException) {
 			log.error("Disconnecting switch {} due to IO Error: {}",
-					getConnectionInfoString(), e.getCause().getMessage());
+					getConnectionInfoString(), cause.getMessage());
 			if (log.isDebugEnabled()) {
 				// still print stack trace if debug is enabled
-				log.debug("StackTrace for previous Exception: ", e.getCause());
+				log.debug("StackTrace for previous Exception: ", cause);
 			}
 			counters.switchDisconnectIOError.increment();
-			ctx.getChannel().close();
-		} else if (e.getCause() instanceof SwitchStateException) {
+			ctx.channel().close();
+		} else if (cause instanceof SwitchStateException) {
 			log.error("Disconnecting switch {} due to switch state error: {}",
-					getConnectionInfoString(), e.getCause().getMessage());
+					getConnectionInfoString(), cause.getMessage());
 			if (log.isDebugEnabled()) {
 				// still print stack trace if debug is enabled
-				log.debug("StackTrace for previous Exception: ", e.getCause());
+				log.debug("StackTrace for previous Exception: ", cause);
 			}
 			counters.switchDisconnectSwitchStateException.increment();
-			ctx.getChannel().close();
-		} else if (e.getCause() instanceof OFAuxException) {
+			ctx.channel().close();
+		} else if (cause instanceof OFAuxException) {
 			log.error("Disconnecting switch {} due to OF Aux error: {}",
-					getConnectionInfoString(), e.getCause().getMessage());
+					getConnectionInfoString(), cause.getMessage());
 			if (log.isDebugEnabled()) {
 				// still print stack trace if debug is enabled
-				log.debug("StackTrace for previous Exception: ", e.getCause());
+				log.debug("StackTrace for previous Exception: ", cause);
 			}
 			counters.switchDisconnectSwitchStateException.increment();
-			ctx.getChannel().close();
-		} else if (e.getCause() instanceof OFParseError) {
+			ctx.channel().close();
+		} else if (cause instanceof OFParseError) {
 			log.error("Disconnecting switch "
 					+ getConnectionInfoString() +
 					" due to message parse failure",
-					e.getCause());
+					cause);
 			counters.switchDisconnectParseError.increment();
-			ctx.getChannel().close();
-		} else if (e.getCause() instanceof RejectedExecutionException) {
+			ctx.channel().close();
+		} else if (cause instanceof RejectedExecutionException) {
 			log.warn("Could not process message: queue full");
 			counters.rejectedExecutionException.increment();
-		} else if (e.getCause() instanceof IllegalArgumentException) {
-			log.error("Illegal argument exception with switch {}. {}", getConnectionInfoString(), e.getCause());
+		} else if (cause instanceof IllegalArgumentException) {
+			log.error("Illegal argument exception with switch {}. {}", getConnectionInfoString(), cause);
 			counters.switchSslConfigurationError.increment();
-			ctx.getChannel().close();
+			ctx.channel().close();
 		} else {
 			log.error("Error while processing message from switch "
 					+ getConnectionInfoString()
-					+ "state " + this.state, e.getCause());
+					+ "state " + this.state, cause);
 			counters.switchDisconnectOtherException.increment();
-			ctx.getChannel().close();
+			ctx.channel().close();
 		}
 	}
 
 	@Override
-	public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
-			throws Exception {
-
+	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
 		log.debug("channelIdle on OFChannelHandler {}", String.format("%08x", System.identityHashCode(this)));
-		OFChannelHandler handler = ctx.getPipeline().get(OFChannelHandler.class);
+		OFChannelHandler handler = ctx.pipeline().get(OFChannelHandler.class);
 		handler.sendEchoRequest();
 	}
 
 	@Override
-	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
-			throws Exception {
-		if (e.getMessage() instanceof List) {
-			@SuppressWarnings("unchecked")
-			List<OFMessage> msglist = (List<OFMessage>)e.getMessage();
-			for (OFMessage ofm : msglist) {
-				try {
-					// Do the actual packet processing
-					state.processOFMessage(ofm);
-				}
-				catch (Exception ex) {
-					// We are the last handler in the stream, so run the
-					// exception through the channel again by passing in
-					// ctx.getChannel().
-					Channels.fireExceptionCaught(ctx.getChannel(), ex);
-				}
+	public void channelRead0(ChannelHandlerContext ctx, Iterable<OFMessage> msgList) throws Exception {
+		for (OFMessage ofm : msgList) {
+			try {
+				// Do the actual packet processing
+				state.processOFMessage(ofm);
+			}
+			catch (Exception ex) {
+				// We are the last handler in the stream, so run the
+				// exception through the channel again by passing in
+				// ctx.getChannel().
+				ctx.fireExceptionCaught(ex);
 			}
-		}
-		else {
-			Channels.fireExceptionCaught(ctx.getChannel(),
-					new AssertionError("Message received from channel is not a list"));
 		}
 	}
 
@@ -731,9 +713,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 	 * This is specifically for aux channels.
 	 */
 	private void setAuxChannelIdle() {
-
 		IdleStateHandler idleHandler = new IdleStateHandler(
-				this.timer,
 				PipelineIdleReadTimeout.AUX,
 				PipelineIdleWriteTimeout.AUX,
 				0);
@@ -765,10 +745,10 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 	private String getConnectionInfoString() {
 
 		String channelString;
-		if (channel == null || channel.getRemoteAddress() == null) {
+		if (channel == null || channel.remoteAddress() == null) {
 			channelString = "?";
 		} else {
-			channelString = channel.getRemoteAddress().toString();
+			channelString = channel.remoteAddress().toString();
 			if(channelString.startsWith("/"))
 				channelString = channelString.substring(1);
 		}
@@ -807,7 +787,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 		OFFeaturesRequest m = factory.buildFeaturesRequest()
 				.setXid(handshakeTransactionIds--)
 				.build();
-		channel.write(Collections.singletonList(m));
+		write(m);
 	}
 
 	/**
@@ -830,8 +810,8 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 
 		OFHello m = builder.setXid(handshakeTransactionIds--)
 				.build();
-		
-		channel.write(Collections.singletonList(m));
+
+		write(m);
 		log.debug("Send hello: {}", m); 
 	}
 
@@ -841,7 +821,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 				.build();
 		/* Record for latency calculation */
 		echoSendTime = System.currentTimeMillis();
-		channel.write(Collections.singletonList(request));
+		write(request);
 	}
 
 	private void sendEchoReply(OFEchoRequest request) {
@@ -849,7 +829,11 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 				.setXid(request.getXid())
 				.setData(request.getData())
 				.build();
-		channel.write(Collections.singletonList(reply));
+		write(reply);
+	}
+	
+	private void write(OFMessage m) {
+		channel.writeAndFlush(Collections.singletonList(m));
 	}
 
 	OFChannelState getStateForTesting() {
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelInfo.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..e4a347e18dafccc9a90f90d48c6e287f1908da02
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelInfo.java
@@ -0,0 +1,59 @@
+package net.floodlightcontroller.core.internal;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+import javax.annotation.Nonnull;
+
+import io.netty.channel.Channel;
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.IPAddress;
+import org.projectfloodlight.openflow.types.OFAuxId;
+
+import com.google.common.base.Preconditions;
+
+/** Basic information that {@link OFChannelHandler} attaches to the
+ *  netty channel via {@link Channel#setAttachment(Object)}, mainly
+ *  for the purpose of being able to log the connection information.
+ *
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ */
+public class OFChannelInfo {
+    private final DatapathId id;
+    private final OFAuxId auxId;
+    private final IPAddress<?> address;
+    private final int port;
+
+    public OFChannelInfo(@Nonnull DatapathId id, @Nonnull OFAuxId auxId, @Nonnull SocketAddress address) {
+        Preconditions.checkNotNull(id, "id should not be null");
+        Preconditions.checkNotNull(auxId, "auxId should not be null");
+        Preconditions.checkNotNull(address, "address should not be null");
+
+        this.id = id;
+        this.auxId = auxId;
+        InetSocketAddress socketAddress = (InetSocketAddress) address;
+        this.address = IPAddress.of(socketAddress.getHostString());
+        this.port = socketAddress.getPort();
+    }
+
+    public DatapathId getId() {
+        return id;
+    }
+
+    public OFAuxId getAuxId() {
+        return auxId;
+    }
+
+    public IPAddress<?> getAddress() {
+       return address;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    @Override
+    public String toString() {
+        return id + "/" + auxId + "@" + address + ":" + port;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelInitializer.java
similarity index 54%
rename from src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java
rename to src/main/java/net/floodlightcontroller/core/internal/OFChannelInitializer.java
index 11827d02fb50a490c7661179bab38e9b2b46292a..3b3709c2e288c5e3fbd31911c7f6248cded12185 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelInitializer.java
@@ -21,7 +21,6 @@ import java.io.FileInputStream;
 import java.security.KeyStore;
 import java.util.List;
 
-import javax.annotation.Nonnull;
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
@@ -29,85 +28,64 @@ import javax.net.ssl.SSLEngine;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.channel.ChannelPipelineFactory;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.handler.ssl.SslHandler;
-import org.jboss.netty.handler.timeout.IdleStateHandler;
-import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
-import org.jboss.netty.util.ExternalResourceReleasable;
-import org.jboss.netty.util.Timer;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.types.U32;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import net.floodlightcontroller.core.internal.IOFSwitchManager;
+import net.floodlightcontroller.core.internal.HandshakeTimeoutHandler;
+import net.floodlightcontroller.core.internal.INewOFConnectionListener;
+import net.floodlightcontroller.core.internal.OFChannelHandler;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.handler.timeout.ReadTimeoutHandler;
+import io.netty.util.Timer;
 
 /**
  * Creates a ChannelPipeline for a server-side openflow channel
- * @author readams, sovietaced
+ * @author readams, sovietaced, rizard, andi-bigswitch
  */
-public class OpenflowPipelineFactory
-implements ChannelPipelineFactory, ExternalResourceReleasable {
-	private static final Logger log = LoggerFactory.getLogger(OpenflowPipelineFactory.class);
-	protected IOFSwitchManager switchManager;
-	protected INewOFConnectionListener connectionListener;
-	protected Timer timer;
-	protected IdleStateHandler idleHandler;
-	protected ReadTimeoutHandler readTimeoutHandler;
-	protected IDebugCounterService debugCounters;
+public class OFChannelInitializer extends ChannelInitializer<Channel> {
+	private static final Logger log = LoggerFactory.getLogger(OFChannelInitializer.class);
+
+	private IOFSwitchManager switchManager;
+	private INewOFConnectionListener connectionListener;
+	private Timer timer;
+	private IDebugCounterService debugCounters;
 	private String keyStore;
 	private String keyStorePassword;
 	private OFFactory defaultFactory;
 	private List<U32> ofBitmaps;
 
-	private void init(IOFSwitchManager switchManager, Timer timer,
+	public OFChannelInitializer(IOFSwitchManager switchManager,
 			INewOFConnectionListener connectionListener,
 			IDebugCounterService debugCounters,
-			@Nonnull List<U32> ofBitmaps,
-			@Nonnull OFFactory defaultFactory) {
+			Timer timer,
+			List<U32> ofBitmaps,
+			OFFactory defaultFactory,
+			String keyStore, 
+			String keyStorePassword) {
+		super();
 		this.switchManager = switchManager;
 		this.connectionListener = connectionListener;
 		this.timer = timer;
 		this.debugCounters = debugCounters;
 		this.defaultFactory = defaultFactory;
 		this.ofBitmaps = ofBitmaps;
-		this.idleHandler = new IdleStateHandler(
-				timer,
-				PipelineIdleReadTimeout.MAIN,
-				PipelineIdleWriteTimeout.MAIN,
-				0);
-		this.readTimeoutHandler = new ReadTimeoutHandler(timer, 30);
-	}
-
-	public OpenflowPipelineFactory(IOFSwitchManager switchManager, Timer timer,
-			INewOFConnectionListener connectionListener,
-			IDebugCounterService debugCounters,
-			@Nonnull List<U32> ofBitmaps,
-			@Nonnull OFFactory defaultFactory) {
-		super();
-		init(switchManager,timer, connectionListener, debugCounters, ofBitmaps, defaultFactory);
-		this.keyStore = null;
-		this.keyStorePassword = null;
-	}
-
-	public OpenflowPipelineFactory(IOFSwitchManager switchManager, Timer timer,
-			INewOFConnectionListener connectionListener,
-			IDebugCounterService debugCounters,
-			@Nonnull List<U32> ofBitmaps,
-			@Nonnull OFFactory defaultFactory,
-			@Nonnull String keyStore, @Nonnull String keyStorePassword) {
-		super();
-		init(switchManager,timer, connectionListener, debugCounters, ofBitmaps, defaultFactory);   
 		this.keyStore = keyStore;
 		this.keyStorePassword = keyStorePassword;
 	}
 
 	@Override
-	public ChannelPipeline getPipeline() throws Exception {
-		ChannelPipeline pipeline = Channels.pipeline();
-		OFChannelHandler handler = new OFChannelHandler(switchManager,
+	protected void initChannel(Channel ch) throws Exception {
+		ChannelPipeline pipeline = ch.pipeline();
+		OFChannelHandler handler = new OFChannelHandler(
+				switchManager,
 				connectionListener,
 				pipeline,
 				debugCounters,
@@ -153,61 +131,57 @@ implements ChannelPipelineFactory, ExternalResourceReleasable {
 				throw e; /* If we wanted secure but didn't get it, we should bail. */
 			}
 		}
-
-		/* SSL handler will have been added first if we're using it. */
+		
 		pipeline.addLast(PipelineHandler.OF_MESSAGE_DECODER,
 				new OFMessageDecoder());
 		pipeline.addLast(PipelineHandler.OF_MESSAGE_ENCODER,
 				new OFMessageEncoder());
-		pipeline.addLast(PipelineHandler.MAIN_IDLE, idleHandler);
-		pipeline.addLast(PipelineHandler.READ_TIMEOUT, readTimeoutHandler);
+		pipeline.addLast(PipelineHandler.MAIN_IDLE,
+				new IdleStateHandler(PipelineIdleReadTimeout.MAIN,
+						PipelineIdleWriteTimeout.MAIN,
+						0));
+		pipeline.addLast(PipelineHandler.READ_TIMEOUT, new ReadTimeoutHandler(30));
 		pipeline.addLast(PipelineHandler.CHANNEL_HANDSHAKE_TIMEOUT,
 				new HandshakeTimeoutHandler(
 						handler,
 						timer,
 						PipelineHandshakeTimeout.CHANNEL));
-		pipeline.addLast(PipelineHandler.CHANNEL_HANDLER, handler);
-		return pipeline;
-	}
 
-	@Override
-	public void releaseExternalResources() {
-		timer.stop();
+		pipeline.addLast(PipelineHandler.CHANNEL_HANDLER, handler);
 	}
 
 	public static class PipelineHandler {
-		final static String CHANNEL_HANDSHAKE_TIMEOUT = "channelhandshaketimeout";
-		final static String SWITCH_HANDSHAKE_TIMEOUT = "switchhandshaketimeout";
-		final static String CHANNEL_HANDLER = "channelhandler";
-		final static String MAIN_IDLE = "mainidle";
-		final static String AUX_IDLE = "auxidle";
-		final static String OF_MESSAGE_DECODER = "ofmessagedecoder";
-		final static String OF_MESSAGE_ENCODER = "ofmessageencoder";
-		final static String READ_TIMEOUT = "readtimeout";
-		final static String SSL_TLS_ENCODER_DECODER = "ofsecurechannelencoderdecoder";
-	}
+		public final static String CHANNEL_HANDSHAKE_TIMEOUT = "channelhandshaketimeout";
+		public final static String SWITCH_HANDSHAKE_TIMEOUT = "switchhandshaketimeout";
+		public final static String CHANNEL_HANDLER = "channelhandler";
+		public final static String MAIN_IDLE = "mainidle";
+		public final static String AUX_IDLE = "auxidle";
+		public final static String OF_MESSAGE_DECODER = "ofmessagedecoder";
+		public final static String OF_MESSAGE_ENCODER = "ofmessageencoder";
+		public final static String READ_TIMEOUT = "readtimeout";
+		public final static String SSL_TLS_ENCODER_DECODER = "ofsecurechannelencoderdecoder";    }
 
 	/**
 	 * Timeouts for parts of the handshake, in seconds
 	 */
-	public static class PipelineHandshakeTimeout {
-		final static int CHANNEL = 10;
-		final static int SWITCH = 30;
-	}
-
-	/**
-	 * Timeouts for writes on connections, in seconds
-	 */
-	public static class PipelineIdleWriteTimeout {
-		final static int MAIN = 2;
-		final static int AUX = 15;
-	}
-
-	/**
-	 * Timeouts for reads on connections, in seconds
-	 */
-	public static class PipelineIdleReadTimeout {
-		final static int MAIN = 3 * PipelineIdleWriteTimeout.MAIN;
-		final static int AUX = 3 * PipelineIdleWriteTimeout.AUX;
-	}
-}
+	 public static class PipelineHandshakeTimeout {
+		 final static int CHANNEL = 10;
+		 public final static int SWITCH = 30;
+	 }
+
+	 /**
+	  * Timeouts for writes on connections, in seconds
+	  */
+	 public static class PipelineIdleWriteTimeout {
+		 final static int MAIN = 2;
+		 public final static int AUX = 15;
+	 }
+
+	 /**
+	  * Timeouts for reads on connections, in seconds
+	  */
+	 public static class PipelineIdleReadTimeout {
+		 final static int MAIN = 3 * PipelineIdleWriteTimeout.MAIN;
+		 public final static int AUX = 3 * PipelineIdleWriteTimeout.AUX;
+	 }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFConnection.java b/src/main/java/net/floodlightcontroller/core/internal/OFConnection.java
new file mode 100644
index 0000000000000000000000000000000000000000..0bf1afadc329daeed35cb92494fdc63f4b9edb56
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFConnection.java
@@ -0,0 +1,452 @@
+/**
+ *    Copyright 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.
+ **/
+
+package net.floodlightcontroller.core.internal;
+
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.Nonnull;
+
+import io.netty.channel.Channel;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
+
+import java.util.Date;
+
+import net.floodlightcontroller.core.Deliverable;
+import net.floodlightcontroller.core.DeliverableListenableFuture;
+import net.floodlightcontroller.core.IOFConnection;
+import net.floodlightcontroller.core.IOFConnectionBackend;
+import net.floodlightcontroller.core.SwitchDisconnectedException;
+import net.floodlightcontroller.debugcounter.IDebugCounterService;
+import net.floodlightcontroller.util.IterableUtils;
+
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFRequest;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
+import org.projectfloodlight.openflow.protocol.OFStatsRequest;
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFAuxId;
+import org.projectfloodlight.openflow.types.U64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Implementation of an openflow connection to switch. Encapsulates a
+ * {@link Channel}, and provides message write and request/response handling
+ * capabilities.
+ *
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ */
+public class OFConnection implements IOFConnection, IOFConnectionBackend{
+	private static final Logger logger = LoggerFactory.getLogger(OFConnection.class);
+	private final DatapathId dpid;
+	private final OFFactory factory;
+
+	/** CAREFUL CAREFUL CAREFUL:
+	 *
+	 * Netty4 does not guarantee order of messages that are written into the channel any more.
+	 * To ensure messages do not get reordered, never directly call {@link Channel#write(Object)}.
+	 *
+	 * Instead, use {@link #write(Iterable)}, which queue up write request on the EventLoop,
+	 * to make sure they are handled in order.
+	 */
+	private final Channel channel;
+
+	private final OFAuxId auxId;
+	private final Timer timer;
+
+	private final Date connectedSince;
+
+	private final Map<Long, Deliverable<?>> xidDeliverableMap;
+
+	private static final long DELIVERABLE_TIME_OUT = 60;
+	private static final TimeUnit DELIVERABLE_TIME_OUT_UNIT = TimeUnit.SECONDS;
+
+	private final OFConnectionCounters counters;
+	private IOFConnectionListener listener;
+
+	private volatile U64 latency;
+
+	/**
+	 * Used to write messages to ensure order w/Netty4.
+	 * It also ensures we do not reuse the array, since
+	 * Netty4 will write the object, not the items.
+	 */
+	private class WriteMessageTask implements Runnable {
+		private final Iterable<OFMessage> msglist;
+
+		public WriteMessageTask(Iterable<OFMessage> msglist) {
+			this.msglist = msglist;
+		}
+
+		@Override
+		public void run() {
+			for (OFMessage m : msglist) {
+				if (logger.isTraceEnabled())
+					logger.trace("{}: send {}", this, m);
+				counters.updateWriteStats(m);
+			}
+			channel.writeAndFlush(msglist);
+		}
+	}
+
+	public OFConnection(@Nonnull DatapathId dpid,
+			@Nonnull OFFactory factory,
+			@Nonnull Channel channel,
+			@Nonnull OFAuxId auxId,
+			@Nonnull IDebugCounterService debugCounters,
+			@Nonnull Timer timer) {
+		Preconditions.checkNotNull(dpid, "dpid");
+		Preconditions.checkNotNull(factory, "factory");
+		Preconditions.checkNotNull(channel, "channel");
+		Preconditions.checkNotNull(timer, "timer");
+		Preconditions.checkNotNull(debugCounters);
+
+		this.listener = NullConnectionListener.INSTANCE;
+		this.dpid = dpid;
+		this.factory = factory;
+		this.channel = channel;
+		this.auxId = auxId;
+		this.connectedSince = new Date();
+		this.xidDeliverableMap = new ConcurrentHashMap<>();
+		this.counters = new OFConnectionCounters(debugCounters, dpid, this.auxId);
+		this.timer = timer;
+		this.latency = U64.ZERO;
+	}
+
+	/**
+	 * All write methods chain into this write() to use WriteMessageTask.
+	 * 
+	 * Write the list of messages to the switch
+	 * 
+	 * @param msgList list of messages to write
+	 * @return list of failed messages; can only fail if channel disconnected
+	 */
+	@Override
+	public Collection<OFMessage> write(final Iterable<OFMessage> msgList) {
+		if (!isConnected()) {
+			if (logger.isDebugEnabled())
+				logger.debug(this.toString() + " : not connected - dropping {} element msglist {} ",
+						Iterables.size(msgList),
+						String.valueOf(msgList).substring(0, 80));
+			return IterableUtils.toCollection(msgList);
+		}
+		for (OFMessage m : msgList) {			
+			if (logger.isTraceEnabled()) {
+				logger.trace("{}: send {}", this, m);
+				counters.updateWriteStats(m);
+			}
+		}
+		this.channel.eventLoop().execute(new WriteMessageTask(msgList));
+		return Collections.emptyList();
+	}
+
+	/**
+	 * Write the single message to the channel
+	 * @param m
+	 * @return true upon success; false upon failure; can only fail if channel disconnected
+	 */
+	@Override
+	public boolean write(OFMessage m) {
+		return this.write(Collections.singletonList(m)).isEmpty();
+	}
+
+	@Override
+	public <R extends OFMessage> ListenableFuture<R> writeRequest(OFRequest<R> request) {
+		if (!isConnected()) {
+			return Futures.immediateFailedFuture(new SwitchDisconnectedException(getDatapathId()));
+		}
+
+		DeliverableListenableFuture<R> future = new DeliverableListenableFuture<R>();
+		xidDeliverableMap.put(request.getXid(), future);
+		listener.messageWritten(this, request);
+		this.write(request);
+		return future;
+	}
+
+	@Override
+	public <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> writeStatsRequest(
+			OFStatsRequest<REPLY> request) {
+		if (!isConnected()) {
+			return Futures.immediateFailedFuture(new SwitchDisconnectedException(getDatapathId()));
+		}
+
+		final DeliverableListenableFuture<List<REPLY>> future =
+				new DeliverableListenableFuture<List<REPLY>>();
+
+		Deliverable<REPLY> deliverable = new Deliverable<REPLY>() {
+			private final List<REPLY> results = Collections
+					.synchronizedList(new ArrayList<REPLY>());
+
+			@Override
+			public void deliver(REPLY reply) {
+				results.add(reply);
+				if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+					// done
+					future.deliver(results);
+				}
+			}
+
+			@Override
+			public void deliverError(Throwable cause) {
+				future.deliverError(cause);
+			}
+
+			@Override
+			public boolean isDone() {
+				return future.isDone();
+			}
+
+			@Override
+			public boolean cancel(boolean mayInterruptIfRunning) {
+				return future.cancel(mayInterruptIfRunning);
+			}
+		};
+
+		registerDeliverable(request.getXid(), deliverable);
+		this.write(request);
+		return future;
+	}
+
+	public void disconnected() {
+		SwitchDisconnectedException exception = new SwitchDisconnectedException(getDatapathId());
+		for (Long xid : xidDeliverableMap.keySet()) {
+			// protect against other mechanisms running at the same time
+			// (timeout)
+			Deliverable<?> removed = xidDeliverableMap.remove(xid);
+			if (removed != null) {
+				removed.deliverError(exception);
+			}
+		}
+	}
+
+	@Override
+	public void disconnect() {
+		this.channel.disconnect();
+		this.counters.uninstallCounters();
+	}
+
+	@Override
+	public String toString() {
+		String channelString = (channel != null) ? String.valueOf(channel.remoteAddress()): "?";
+		return "OFConnection [" + getDatapathId() + "(" + getAuxId() + ")" + "@" + channelString + "]";
+	}
+
+	@Override
+	public Date getConnectedSince() {
+		return connectedSince;
+	}
+
+	private void registerDeliverable(long xid, Deliverable<?> deliverable) {
+		this.xidDeliverableMap.put(xid, deliverable);
+		timer.newTimeout(new TimeOutDeliverable(xid), DELIVERABLE_TIME_OUT, DELIVERABLE_TIME_OUT_UNIT);
+	}
+
+	public boolean handleGenericDeliverable(OFMessage reply) {
+		counters.updateReadStats(reply);
+		@SuppressWarnings("unchecked")
+		Deliverable<OFMessage> deliverable =
+		(Deliverable<OFMessage>) this.xidDeliverableMap.get(reply.getXid());
+		if (deliverable != null) {
+			if(reply instanceof OFErrorMsg) {
+				deliverable.deliverError(new OFErrorMsgException((OFErrorMsg) reply));
+			} else {
+				deliverable.deliver(reply);
+			}
+			if (deliverable.isDone())
+				this.xidDeliverableMap.remove(reply.getXid());
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	@Override
+	public void cancelAllPendingRequests() {
+		/*
+		 * we don't need to be synchronized here. Even if another thread
+		 * modifies the map while we're cleaning up the future will eventually
+		 * timeout
+		 */
+		for (Deliverable<?> d : xidDeliverableMap.values()) {
+			d.cancel(true);
+		}
+		xidDeliverableMap.clear();
+	}
+
+	@Override
+	public boolean isConnected() {
+		return channel.isActive();
+	}
+
+	@Override
+	public SocketAddress getRemoteInetAddress() {
+		return channel.remoteAddress();
+	}
+
+	@Override
+	public SocketAddress getLocalInetAddress() {
+		return channel.localAddress();
+	}
+
+	public boolean deliverResponse(OFMessage m) {
+		if (handleGenericDeliverable(m))
+			return true;
+		else
+			return false;
+	}
+
+	@Override
+	public boolean isWritable() {
+		return channel.isWritable();
+	}
+
+	@Override
+	public DatapathId getDatapathId() {
+		return dpid;
+	}
+
+	@Override
+	public OFAuxId getAuxId() {
+		return auxId;
+	}
+
+	Set<Long> getPendingRequestIds() {
+		return ImmutableSet.copyOf(xidDeliverableMap.keySet());
+	}
+
+	@Override
+	public OFFactory getOFFactory() {
+		return this.factory;
+	}
+
+	/**
+	 * Timeout class instantiated for deliverables. Will throw a timeout exception
+	 * if proper responses are not received in time.
+	 *
+	 */
+	private class TimeOutDeliverable implements TimerTask {
+		private final long xid;
+
+		public TimeOutDeliverable(long xid) {
+			this.xid = xid;
+		}
+
+		@Override
+		public void run(Timeout timeout) throws Exception {
+			Deliverable<?> removed = xidDeliverableMap.remove(xid);
+			if (removed != null && !removed.isDone()) {
+				removed.deliverError(new TimeoutException(
+						"timeout - did not receive answer for xid " + xid));
+			}
+
+		}
+	}
+
+	public IOFConnectionListener getListener() {
+		return listener;
+	}
+
+	/** set the connection listener
+	 *  <p>
+	 *  Note: this is assumed to be called from the Connection's IO Thread.
+	 *
+	 * @param listener
+	 */
+	@Override
+	public void setListener(IOFConnectionListener listener) {
+		this.listener = listener;
+	}
+
+	public void messageReceived(OFMessage m) {
+		// Check if message was a response for a xid waiting at the switch
+		if(!deliverResponse(m)){
+			listener.messageReceived(this, m);
+		}
+	}
+
+	@Override
+	public U64 getLatency() {
+		return this.latency;
+	}
+
+	@Override
+	public void updateLatency(U64 latency) {
+		if (latency == null) {
+			logger.error("Latency must be non-null. Ignoring null latency value.");
+			return;
+		} else if (this.latency.equals(U64.ZERO)) { 
+			logger.debug("Recording previously 0ms switch {} latency as {}ms", this.getDatapathId(), latency.getValue());
+			this.latency = latency;
+			return;
+		} else {
+			double oldWeight = 0.30;
+			this.latency = U64.of((long) (this.latency.getValue() * oldWeight + latency.getValue() * (1 - oldWeight)));
+			logger.debug("Switch {} latency updated to {}ms", this.getDatapathId(), this.latency.getValue());
+		}
+	}
+
+	/** A dummy connection listener that just logs warn messages. Saves us a few null checks
+	 * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+	 */
+	private static class NullConnectionListener implements IOFConnectionListener {
+		public final static NullConnectionListener INSTANCE = new NullConnectionListener();
+
+		private NullConnectionListener() { }
+
+		@Override
+		public void connectionClosed(IOFConnectionBackend connection) {
+			logger.warn("NullConnectionListener for {} - received connectionClosed", connection);
+		}
+
+		@Override
+		public void messageReceived(IOFConnectionBackend connection, OFMessage m) {
+			logger.warn("NullConnectionListener for {} - received messageReceived: {}", connection, m);
+		}
+
+		@Override
+		public boolean isSwitchHandshakeComplete(IOFConnectionBackend connection) {
+			return false;
+		}
+
+		@Override
+		public void messageWritten(IOFConnectionBackend connection, OFMessage m) {
+			// TODO Auto-generated method stub
+
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/OFConnectionCounters.java b/src/main/java/net/floodlightcontroller/core/internal/OFConnectionCounters.java
similarity index 99%
rename from src/main/java/net/floodlightcontroller/core/OFConnectionCounters.java
rename to src/main/java/net/floodlightcontroller/core/internal/OFConnectionCounters.java
index aef32b24d9743be5bf8528d3c02714cfdf425eb4..b92ab9ed71a031376f0805b0723c55ea6dcd7570 100644
--- a/src/main/java/net/floodlightcontroller/core/OFConnectionCounters.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFConnectionCounters.java
@@ -1,6 +1,5 @@
-package net.floodlightcontroller.core;
+package net.floodlightcontroller.core.internal;
 
-import net.floodlightcontroller.core.internal.OFSwitchManager;
 import net.floodlightcontroller.debugcounter.IDebugCounter;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 
diff --git a/src/main/java/net/floodlightcontroller/core/OFErrorMsgException.java b/src/main/java/net/floodlightcontroller/core/internal/OFErrorMsgException.java
similarity index 95%
rename from src/main/java/net/floodlightcontroller/core/OFErrorMsgException.java
rename to src/main/java/net/floodlightcontroller/core/internal/OFErrorMsgException.java
index f990f0a451052dfac060673e5bca7b20d6e802ad..bdb526153ac5cc2ce6b9ebf7d0aac2d6d4bfc276 100644
--- a/src/main/java/net/floodlightcontroller/core/OFErrorMsgException.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFErrorMsgException.java
@@ -1,4 +1,4 @@
-package net.floodlightcontroller.core;
+package net.floodlightcontroller.core.internal;
 
 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
 import org.projectfloodlight.openflow.protocol.OFRequest;
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java b/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java
index 12faa3be6b49a43d70a8d8fbffec769184524b77..40e6434baeef2c7538437f02ca674b70894f1487 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java
@@ -1,29 +1,30 @@
 /**
-*    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.
-**/
+ *    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.core.internal;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFMessage;
@@ -33,49 +34,71 @@ import org.projectfloodlight.openflow.protocol.OFVersion;
 /**
  * Decode an openflow message from a channel, for use in a netty pipeline.
  *
- * @author readams
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
  */
-public class OFMessageDecoder extends FrameDecoder {
+public class OFMessageDecoder extends ByteToMessageDecoder {
+
+	private OFMessageReader<OFMessage> reader;
 
-    private OFMessageReader<OFMessage> reader;
+	public OFMessageDecoder() {
+		setReader();
+	}
 
-    public OFMessageDecoder() {
-        reader = OFFactories.getGenericReader();
-    }
+	public OFMessageDecoder(OFVersion version) {
+		setVersion(version);
+		setReader();
+	}
 
-    public OFMessageDecoder(OFVersion version) {
-        setVersion(version);
-    }
+	private void setReader() {
+		reader = OFFactories.getGenericReader();
+	}
 
-    public void setVersion(OFVersion version) {
-        OFFactory factory = OFFactories.getFactory(version);
-        this.reader = factory.getReader();
-    }
+	public void setVersion(OFVersion version) {
+		OFFactory factory = OFFactories.getFactory(version);
+		this.reader = factory.getReader();
+	}
 
-    @Override
-    protected Object decode(ChannelHandlerContext ctx, Channel channel,
-                            ChannelBuffer buffer) throws Exception {
-        if (!channel.isConnected()) {
-            // In testing, I see decode being called AFTER decode last.
-            // This check avoids that from reading corrupted frames
-            return null;
-        }
+	@Override
+	protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+		if (!ctx.channel().isActive()) {
+			// In testing, I see decode being called AFTER decode last.
+			// This check avoids that from reading corrupted frames
+			return;
+		}
 
-        List<OFMessage> messageList = new ArrayList<OFMessage>();
-        for (;;) {
-            OFMessage message = reader.readFrom(buffer);
-            if (message == null)
-                break;
-            messageList.add(message);
-        }
-        return messageList.isEmpty() ? null : messageList;
-    }
+		// Note(andiw): netty4 adds support for more efficient handling of lists messages in the
+		// pipeline itself.
+		// Instead of constructing a list of messages here, we could also just add the individual
+		// messages to the "out" list provided by netty. This would require changing all the handlers
+		// in the pipeline to accept "OFMessage" instead of "Iterable<OFMessage>". Probably
+		// a good idea, but left for a future cleanup.
 
-    @Override
-    protected Object decodeLast(ChannelHandlerContext ctx, Channel channel,
-                            ChannelBuffer buffer) throws Exception {
-        // This is not strictly needed at this time. It is used to detect
-        // connection reset detection from netty (for debug)
-        return null;
-    }
-}
+		OFMessage singleMessage = null;
+		List<OFMessage> list = null;
+		boolean first = true;
+		for (;;) {
+			OFMessage message = reader.readFrom(in);
+			if (message == null) {
+				break;
+			}
+			if (first) {
+				// first message read
+				singleMessage = message;
+				first = false;
+			} else {
+				// more messages read, use the list
+				if (list == null) {
+					list = new ArrayList<>();
+					list.add(singleMessage);
+					singleMessage = null;
+				}
+				list.add(message);
+			}
+		}
+		if (list != null) {
+			out.add(list);
+		} else if (singleMessage != null) {
+			out.add(Collections.singletonList(singleMessage));
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFMessageEncoder.java b/src/main/java/net/floodlightcontroller/core/internal/OFMessageEncoder.java
index 4d6912ad69a9246da975063127fca3b8d97fe0b0..09ca7bd4441b89150d1417c508080243cd98765f 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFMessageEncoder.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFMessageEncoder.java
@@ -17,33 +17,24 @@
 
 package net.floodlightcontroller.core.internal;
 
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
+
 /**
- * Encode an openflow message for output into a ChannelBuffer, for use in a
+ * Encode an iterable of openflow messages for output into a ByteBuf, for use in a
  * netty pipeline
- * @author readams
+ *
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
  */
-public class OFMessageEncoder extends OneToOneEncoder {
-
+public class OFMessageEncoder extends MessageToByteEncoder<Iterable<OFMessage>> {
     @Override
-    protected Object encode(ChannelHandlerContext ctx, Channel channel,
-                            Object msg) throws Exception {
-        if (!(msg instanceof Iterable))
-            return msg;
-
-        @SuppressWarnings("unchecked")
-        Iterable<OFMessage> msgList = (Iterable<OFMessage>)msg;
-
-        ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+    protected void encode(ChannelHandlerContext ctx, Iterable<OFMessage> msgList, ByteBuf out) throws Exception {
         for (OFMessage ofm :  msgList) {
-            ofm.writeTo(buf);
+            ofm.writeTo(out);
         }
-        return buf;
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitch.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitch.java
similarity index 86%
rename from src/main/java/net/floodlightcontroller/core/OFSwitch.java
rename to src/main/java/net/floodlightcontroller/core/internal/OFSwitch.java
index 1b72fef170fd0a8b3b5827dd1ce39bc747dad533..b31c379666c451b181fa630f2634cd1b711be4d4 100644
--- a/src/main/java/net/floodlightcontroller/core/OFSwitch.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitch.java
@@ -15,7 +15,7 @@
  *    under the License.
  **/
 
-package net.floodlightcontroller.core;
+package net.floodlightcontroller.core.internal;
 
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
@@ -27,6 +27,8 @@ import java.util.Comparator;
 import java.util.Date;
 import java.util.EnumSet;
 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;
@@ -37,8 +39,16 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import javax.annotation.Nonnull;
 
-import net.floodlightcontroller.core.internal.IOFSwitchManager;
-import net.floodlightcontroller.core.internal.TableFeatures;
+import net.floodlightcontroller.core.IOFConnection;
+import net.floodlightcontroller.core.IOFConnectionBackend;
+import net.floodlightcontroller.core.IOFSwitchBackend;
+import net.floodlightcontroller.core.LogicalOFMessageCategory;
+import net.floodlightcontroller.core.PortChangeEvent;
+import net.floodlightcontroller.core.PortChangeType;
+import net.floodlightcontroller.core.SwitchDescription;
+import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted;
+import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted;
+import net.floodlightcontroller.core.SwitchDriverSubHandshakeNotStarted;
 import net.floodlightcontroller.core.util.AppCookie;
 import net.floodlightcontroller.core.util.URIUtil;
 
@@ -63,6 +73,7 @@ import org.projectfloodlight.openflow.protocol.OFStatsReply;
 import org.projectfloodlight.openflow.protocol.OFStatsRequest;
 import org.projectfloodlight.openflow.protocol.OFTableFeatures;
 import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsReply;
+import org.projectfloodlight.openflow.protocol.OFType;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFAuxId;
@@ -70,6 +81,7 @@ import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.TableId;
 import org.projectfloodlight.openflow.types.U64;
 
+import net.floodlightcontroller.util.IterableUtils;
 import net.floodlightcontroller.util.LinkedHashSetWrapper;
 import net.floodlightcontroller.util.OrderedCollection;
 
@@ -669,6 +681,76 @@ public class OFSwitch implements IOFSwitchBackend {
 		}
 	}
 
+	protected static class SwitchRoleMessageValidator {
+		private static final Map<OFVersion, Set<OFType>> invalidSlaveMsgsByOFVersion;
+		static {
+			Map<OFVersion, Set<OFType>> m = new HashMap<OFVersion, Set<OFType>>();
+			Set<OFType> s = new HashSet<OFType>();
+			s.add(OFType.PACKET_OUT);
+			s.add(OFType.FLOW_MOD);
+			s.add(OFType.PORT_MOD);
+			s.add(OFType.TABLE_MOD);
+			s.add(OFType.BARRIER_REQUEST);
+			m.put(OFVersion.OF_10, Collections.unmodifiableSet(s));
+			
+			s = new HashSet<OFType>();
+			s.addAll(m.get(OFVersion.OF_10));
+			s.add(OFType.GROUP_MOD);
+			s.add(OFType.TABLE_MOD);
+			m.put(OFVersion.OF_11, Collections.unmodifiableSet(s));
+			
+			s = new HashSet<OFType>();
+			s.addAll(m.get(OFVersion.OF_11));
+			m.put(OFVersion.OF_12, Collections.unmodifiableSet(s));
+			
+			s = new HashSet<OFType>();
+			s.addAll(m.get(OFVersion.OF_12));
+			s.add(OFType.METER_MOD);
+			m.put(OFVersion.OF_13, Collections.unmodifiableSet(s));
+			
+			s = new HashSet<OFType>();
+			s.addAll(m.get(OFVersion.OF_13));
+			s.add(OFType.BUNDLE_ADD_MESSAGE);
+			s.add(OFType.BUNDLE_CONTROL);
+			m.put(OFVersion.OF_14, Collections.unmodifiableSet(s));
+
+			invalidSlaveMsgsByOFVersion = Collections.unmodifiableMap(m);
+		}
+
+		/**
+		 * Sorts any invalid messages by moving them from the msgList. The net result
+		 * is a new list returned containing the invalid messages and a pruned msgList
+		 * containing only those messages that are valid for the given role of the controller
+		 * and OpenFlow version of the switch.
+		 * 
+		 * @param msgList the list of messages to sort
+		 * @param valid the list of valid messages (caller must allocate)
+		 * @param swVersion the OFVersion of the switch
+		 * @param isSlave true if controller is slave; false otherwise
+		 * @return list of messages that are not valid, removed from input parameter msgList
+		 */
+		protected static Collection<OFMessage> pruneInvalidMessages(Iterable<OFMessage> msgList, Collection<OFMessage> valid, OFVersion swVersion, boolean isActive) {
+			if (isActive) { /* master or equal/other support all */
+				valid.addAll(IterableUtils.toCollection(msgList));
+				return Collections.emptyList();
+			} else { /* slave */
+				Set<OFType> invalidSlaveMsgs = invalidSlaveMsgsByOFVersion.get(swVersion);
+				List<OFMessage> invalid = new ArrayList<OFMessage>();
+				Iterator<OFMessage> itr = msgList.iterator();
+				while (itr.hasNext()) {
+					OFMessage m = itr.next();
+					if (invalidSlaveMsgs.contains(m.getType())) {
+						invalid.add(m);
+					} else {
+						valid.add(m);
+					}
+				}
+
+				return invalid;
+			}
+		}
+	}
+
 	@Override
 	public boolean attributeEquals(String name, Object other) {
 		Object attr = this.attributes.get(name);
@@ -720,17 +802,6 @@ public class OFSwitch implements IOFSwitchBackend {
 		this.connections.remove(connection.getAuxId());
 	}
 
-	@Override
-	public void write(OFMessage m) {
-		log.trace("Channel: {}, Connected: {}", connections.get(OFAuxId.MAIN).getRemoteInetAddress(), connections.get(OFAuxId.MAIN).isConnected());
-		if (isActive()) {
-			connections.get(OFAuxId.MAIN).write(m);
-			switchManager.handleOutgoingMessage(this, m);
-		} else {
-			log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString());
-		}
-	}
-
 	/**
 	 * Gets a connection specified by aux Id.
 	 * @param auxId the specified aux id for the connection desired.
@@ -738,14 +809,14 @@ public class OFSwitch implements IOFSwitchBackend {
 	 */
 	public IOFConnection getConnection(OFAuxId auxId) {
 		IOFConnection connection = this.connections.get(auxId);
-		if(connection == null){
+		if (connection == null) {
 			throw new IllegalArgumentException("OF Connection for " + this + " with " + auxId + " does not exist.");
 		}
 		return connection;
 	}
 
 	public IOFConnection getConnection(LogicalOFMessageCategory category) {
-		if(switchManager.isCategoryRegistered(category)){
+		if (switchManager.isCategoryRegistered(category)) {
 			return getConnection(category.getAuxId());
 		}
 		else{
@@ -753,26 +824,72 @@ public class OFSwitch implements IOFSwitchBackend {
 		}
 	}
 
+	/**
+	 * Write a single message to the switch
+	 * 
+	 * @param m the message to write
+	 * @return true upon success; false upon failure;
+	 * failure can occur either from sending a message not supported in the current role, or
+	 * from the channel being disconnected
+	 */
 	@Override
-	public void write(OFMessage m, LogicalOFMessageCategory category) {
-		if (isActive()) {
-			this.getConnection(category).write(m);
-			switchManager.handleOutgoingMessage(this, m);
-		} else {
-			log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString());
-		}
+	public boolean write(OFMessage m) {
+		return this.write(Collections.singletonList(m)).isEmpty();
 	}
 
+
+	/**
+	 * Write a list of messages to the switch
+	 * 
+	 * @param msglist list of messages to write
+	 * @return list of failed messages; messages can fail if sending the messages is not supported
+	 * in the current role, or from the channel becoming disconnected
+	 */
 	@Override
-	public void write(Iterable<OFMessage> msglist, LogicalOFMessageCategory category) {
-		if (isActive()) {
-			this.getConnection(category).write(msglist);
+	public Collection<OFMessage> write(Iterable<OFMessage> msglist) {
+		return this.write(msglist, LogicalOFMessageCategory.MAIN);
+	}
 
-			for(OFMessage m : msglist) {
-				switchManager.handleOutgoingMessage(this, m);				
+	@Override
+	public boolean write(OFMessage m, LogicalOFMessageCategory category) {
+		return this.write(Collections.singletonList(m), category).isEmpty();
+	}
+
+	@Override
+	public Collection<OFMessage> write(Iterable<OFMessage> msgList, LogicalOFMessageCategory category) {
+		IOFConnection conn = this.getConnection(category); /* do first to check for supported category */
+		Collection<OFMessage> validMsgs = new ArrayList<OFMessage>();
+		Collection<OFMessage> invalidMsgs = SwitchRoleMessageValidator.pruneInvalidMessages(
+				msgList, validMsgs, this.getOFFactory().getVersion(), this.isActive());
+		if (log.isDebugEnabled()) {
+			log.debug("MESSAGES: {}, VALID: {}, INVALID: {}", new Object[] { msgList, validMsgs, invalidMsgs});
+		}
+		/* Try to write all valid messages */
+		Collection<OFMessage> unsent = conn.write(validMsgs);
+		for (OFMessage m : validMsgs) {
+			if (!unsent.contains(m)) {
+				switchManager.handleOutgoingMessage(this, m);
 			}
+		}
+		
+		/* Collect invalid and unsent messages */
+		Collection<OFMessage> ret = null;
+		if (!unsent.isEmpty()) {
+			log.warn("Could not send messages {} due to channel disconnection on switch {}", unsent, this.getId());
+			ret = IterableUtils.toCollection(unsent);
+		}
+		if (!invalidMsgs.isEmpty()) {
+			log.warn("Could not send messages {} while in SLAVE role on switch {}", invalidMsgs, this.getId());
+			if (ret == null) {
+				ret = IterableUtils.toCollection(invalidMsgs);
+			} else {
+				ret.addAll(IterableUtils.toCollection(invalidMsgs));
+			}
+		}
+		if (ret == null) {
+			return Collections.emptyList();
 		} else {
-			log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString());
+			return ret;
 		}
 	}
 
@@ -788,27 +905,13 @@ public class OFSwitch implements IOFSwitchBackend {
 
 	@Override
 	public <R extends OFMessage> ListenableFuture<R> writeRequest(OFRequest<R> request) {
-		return connections.get(OFAuxId.MAIN).writeRequest(request);
-	}
-
-	@Override
-	public void write(Iterable<OFMessage> msglist) {
-		if (isActive()) {
-			connections.get(OFAuxId.MAIN).write(msglist);
-
-			for(OFMessage m : msglist) {
-				switchManager.handleOutgoingMessage(this, m);
-			}
-		} else {
-			log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString());
-		}
+		return writeRequest(request, LogicalOFMessageCategory.MAIN);
 	}
 
 	@Override
 	public void disconnect() {
-
 		// Iterate through connections and perform cleanup
-		for(Entry<OFAuxId, IOFConnectionBackend> entry : this.connections.entrySet()){
+		for (Entry<OFAuxId, IOFConnectionBackend> entry : this.connections.entrySet()) {
 			entry.getValue().disconnect();
 			this.connections.remove(entry.getKey());
 		}
@@ -943,12 +1046,9 @@ public class OFSwitch implements IOFSwitchBackend {
 		return datapathId;
 	}
 
-	/* (non-Javadoc)
-	 * @see java.lang.Object#toString()
-	 */
 	@Override
 	public String toString() {
-		return "OFSwitchBase DPID[" + ((datapathId != null) ? datapathId.toString() : "?") + "]";
+		return "OFSwitch DPID[" + ((datapathId != null) ? datapathId.toString() : "?") + "]";
 	}
 
 	@Override
@@ -1057,13 +1157,6 @@ public class OFSwitch implements IOFSwitchBackend {
 		this.role = role;
 	}
 
-	@Override
-	public void flush() {
-		for(Entry<OFAuxId, IOFConnectionBackend> entry : this.connections.entrySet()){
-			entry.getValue().flush();
-		}
-	}
-
 	/**
 	 * Get the IP Address for the switch
 	 * @return the inet address
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchAppHandshakePlugin.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchAppHandshakePlugin.java
index ddfd008720a6853262905afeb7072cf91c99c3f5..9652f4ac64d60b1e5f31bb2e31b1023fd12120c7 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchAppHandshakePlugin.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchAppHandshakePlugin.java
@@ -2,9 +2,9 @@ package net.floodlightcontroller.core.internal;
 
 import java.util.concurrent.TimeUnit;
 
-import org.jboss.netty.util.Timeout;
-import org.jboss.netty.util.Timer;
-import org.jboss.netty.util.TimerTask;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.WaitAppHandshakeState;
 import org.projectfloodlight.openflow.protocol.OFMessage;
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
index 9621b995b44892ab6d1ca1ba4307b8f15ab71650..47671f5cedd4b502bfc8aa69a7354943568d62c2 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
@@ -12,8 +12,7 @@ import java.util.concurrent.TimeUnit;
 
 import javax.annotation.Nonnull;
 
-import org.jboss.netty.util.Timer;
-
+import io.netty.util.Timer;
 import net.floodlightcontroller.core.HARole;
 import net.floodlightcontroller.core.IOFConnection;
 import net.floodlightcontroller.core.IOFConnectionBackend;
@@ -23,13 +22,11 @@ import net.floodlightcontroller.core.IOFSwitchBackend;
 import net.floodlightcontroller.core.PortChangeEvent;
 import net.floodlightcontroller.core.SwitchDescription;
 import net.floodlightcontroller.core.internal.OFSwitchAppHandshakePlugin.PluginResultType;
-import net.floodlightcontroller.util.OFDPAUtils;
 
 import org.projectfloodlight.openflow.protocol.OFActionType;
 import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
 import org.projectfloodlight.openflow.protocol.OFBarrierReply;
 import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
-import org.projectfloodlight.openflow.protocol.OFBucket;
 import org.projectfloodlight.openflow.protocol.OFControllerRole;
 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
@@ -46,7 +43,6 @@ 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.OFGroupAdd;
 import org.projectfloodlight.openflow.protocol.OFGroupDelete;
 import org.projectfloodlight.openflow.protocol.OFGroupType;
 import org.projectfloodlight.openflow.protocol.OFMessage;
@@ -54,7 +50,6 @@ import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole;
 import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply;
 import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
-import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
 import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
@@ -74,17 +69,12 @@ import org.projectfloodlight.openflow.protocol.actionid.OFActionId;
 import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
 import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
-import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFAuxId;
-import org.projectfloodlight.openflow.types.OFBufferId;
 import org.projectfloodlight.openflow.types.OFGroup;
 import org.projectfloodlight.openflow.types.OFPort;
-import org.projectfloodlight.openflow.types.OFVlanVidMatch;
 import org.projectfloodlight.openflow.types.TableId;
-import org.projectfloodlight.openflow.types.U16;
 import org.projectfloodlight.openflow.types.U64;
-import org.projectfloodlight.openflow.types.VlanVid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -529,7 +519,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 								OFFlowAdd defaultFlow = this.factory.buildFlowAdd()
 										.setTableId(tid)
 										.setPriority(0)
-										.setActions(actions)
+										.setInstructions(Collections.singletonList((OFInstruction) this.factory.instructions().buildApplyActions().setActions(actions).build()))
 										.build();
 								flows.add(defaultFlow);
 								break; /* Stop searching for actions and go to the next table in the list */
@@ -555,142 +545,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 		}
 	}
 
-	private void addBroadcomOFDPAFlows() {
-		/*
-		 * By default, we'll assume everyone's on the same VLAN,
-		 * and all switch ports are configured as access ports.
-		 * As such, all packets on the wire will be untagged
-		 * and will only be tagged internally in the switch for 
-		 * pipeline processing.
-		 * 
-		 * If you would like to configure trunks on switches, then
-		 * each switch will need to be configured specifically, as
-		 * we won't be able to automatically handle such a topology.
-		 * 
-		 * Ingress port table (0)     = empty --> default to VLAN table
-		 * VLAN table (10)            = match untagged, apply internal tag, goto termination MAC
-		 * Termination MAC table (20) = match vlan tag and dst MAC, goto bridging table (default miss-->bridging)
-		 * 							  	match only vlan tag, goto controller (DLF or Dest Lookup Failure)
-		 * Bridging table (50)        = default send to policy ACL table
-		 * Policy ACL table (60)      = priority=0 go to controller
-		 *                              write action group of forwarding decision
-		 * Group tables
-		 *   One per interface per VLAN
-		 *   One per VLAN (for flooding)
-		 *   
-		 *  TABLE_INGRESS = 0
-		 *  TABLE_VLAN = 10
-		 *  TABLE_MAC = 20
-		 *  TABLE_UNICAST = 30
-		 *  TABLE_MULTICAST = 40
-		 *  TABLE_BRIDGING = 50
-		 *  TABLE_ACL = 60
-		 */
-
-		/*
-		 * Add flow to match all untagged packets from all ports in VLAN table
-		 */
-		List<OFAction> al = new ArrayList<OFAction>(1);
-		/* al.add(factory.actions().pushVlan(EthType.IPv4)); might not need this */
-		al.add(factory.actions().setVlanVid(VlanVid.ofVlan(1))); /* we'll use 1 internally, just because */
-
-		List<OFInstruction> il = new ArrayList<OFInstruction>(2);
-		il.add(factory.instructions().gotoTable(TableId.of(20))); /* 20 is the termination MAC table */
-		il.add(factory.instructions().applyActions(al));
-		OFFlowAdd fa = factory.buildFlowAdd()
-				.setTableId(TableId.of(10))
-				.setOutPort(OFPort.ANY)
-				.setBufferId(OFBufferId.NO_BUFFER)
-				.setCookie(U64.ZERO)
-				.setMatch(factory.buildMatch()
-						.setExact(MatchField.VLAN_VID, OFVlanVidMatch.UNTAGGED) /* this flow handles untagged */
-						/* do we have to match on the in port here? */
-						.build()
-						)
-						.setInstructions(il)
-						.setPriority(1000)
-						.build();
-		sw.write(fa);
-
-		/*
-		 * The termination MAC flow table must proactively forward to controller specific dst MACs,
-		 * so we need to wait to do wildcarded dst MACs in bridging table upon a miss. Send to bridging
-		 * table by default here.
-		 */
-
-		/*
-		 * Add flow to match all vlan=1 packets to forward to controller in bridging table (DLF).
-		 * Default is to send to policy ACL if this does not match.
-		 */
-		al = new ArrayList<OFAction>(1);
-		al.add(factory.actions().output(OFPort.CONTROLLER, 0xffFFffFF));
-
-		il = new ArrayList<OFInstruction>(1);
-		il.add(factory.instructions().applyActions(al));
-		fa = factory.buildFlowAdd()
-				.setTableId(TableId.of(50))
-				.setOutPort(OFPort.ANY)
-				.setBufferId(OFBufferId.NO_BUFFER)
-				.setCookie(U64.ZERO)
-				.setMatch(factory.buildMatch()
-						.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(1)) /* this flow handles recently-tagged VLAN=1 */
-						.build()
-						)
-						.setInstructions(il) 
-						.setPriority(1)
-						.build();
-		sw.write(fa);
-
-		/*
-		 * Lastly, add a group for flooding and for each port.
-		 * This group is only for VLAN=1.
-		 * 
-		 * The flood group has buckets with goto group actions
-		 * for each port's individual L2 group for VLAN=1.
-		 * 
-		 * This means we must first add the individual groups.
-		 */
-		ArrayList<OFBucket> buckets = new ArrayList<OFBucket>();
-		for (OFPortDesc pd : this.sw.getPorts()) {
-			OFPort p = pd.getPortNo();
-			if ((p.getShortPortNumber() & 0xFF00) == 0) { /* TODO Is this correct for special ports? */
-				OFGroupAdd ga = factory.buildGroupAdd()
-						.setGroupType(OFGroupType.INDIRECT)
-						.setBuckets(Collections.singletonList(factory.buildBucket()
-								.setActions(
-										Collections.singletonList((OFAction) factory.actions().buildOutput()
-												.setMaxLen(0xffFFffFF)
-												.setPort(p)
-												.build()))
-												.build()))
-												.setGroup(OFDPAUtils.GroupIds.createL2Interface(p, VlanVid.ofVlan(100)))
-												.build();
-				sw.write(ga);
-
-				/*
-				 * Add the port+bucket for creating the FLOOD group below.
-				 * All L2_INTERFACE groups in a VLAN should be within a
-				 * corresponding L2_FLOOD group of type ALL.
-				 */
-				buckets.add(factory.buildBucket().setActions(
-						Collections.singletonList(
-								(OFAction) factory.actions().buildOutput()
-								.setMaxLen(0xffFFffFF)
-								.setPort(p)
-								.build()
-								)
-						).build());
-			}
-		}
-
-		OFGroupAdd ga = factory.buildGroupAdd()
-				.setGroupType(OFGroupType.ALL)
-				.setBuckets(buckets)
-				.setGroup(OFDPAUtils.GroupIds.createL2Flood(U16.ZERO, VlanVid.ofVlan(100)))
-				.build();
-		sw.write(ga);
-	}
-
 	/**
 	 * Default implementation for message handlers in any state.
 	 *
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java
index dbf9cc2449c9cfe77cf562296071662f5f478c61..9b22851539d7f2e344514bcc9583a7ea23544145 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java
@@ -1,6 +1,7 @@
 package net.floodlightcontroller.core.internal;
 
 import java.io.IOException;
+import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -16,13 +17,6 @@ import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.Executors;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipelineFactory;
-import org.jboss.netty.channel.group.ChannelGroup;
-import org.jboss.netty.channel.group.DefaultChannelGroup;
-import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
 
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.HAListenerTypeMarker;
@@ -60,6 +54,7 @@ import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
 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.U32;
@@ -80,6 +75,13 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 
+import io.netty.bootstrap.ServerBootstrap;
+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.concurrent.GlobalEventExecutor;
+
 /**
  * The Switch Manager class contains most of the code involved with dealing
  * with switches. The Switch manager keeps track of the switches known to the controller,
@@ -102,7 +104,6 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 
 	private static String keyStorePassword;
 	private static String keyStore;
-	private static boolean useSsl = false;
 
 	protected static boolean clearTablesOnInitialConnectAsMaster = false;
 	protected static boolean clearTablesOnEachTransitionToMaster = false;
@@ -112,7 +113,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 
 	protected static List<U32> ofBitmaps;
 	protected static OFFactory defaultFactory;
-	
+
 	private ConcurrentHashMap<DatapathId, OFSwitchHandshakeHandler> switchHandlers;
 	private ConcurrentHashMap<DatapathId, IOFSwitchBackend> switches;
 	private ConcurrentHashMap<DatapathId, IOFSwitch> syncedSwitches;
@@ -129,9 +130,13 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 	protected Set<IOFSwitchListener> switchListeners;
 
 	// Module Dependencies
-	IFloodlightProviderService floodlightProvider;
-	IDebugEventService debugEventService;
-	IDebugCounterService debugCounterService;
+	private IFloodlightProviderService floodlightProvider;
+	private IDebugEventService debugEventService;
+	private IDebugCounterService debugCounterService;
+
+	private NioEventLoopGroup bossGroup;
+	private NioEventLoopGroup workerGroup;
+	private DefaultChannelGroup cg;
 
 	/** IHAListener Implementation **/
 	@Override
@@ -181,7 +186,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 			addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED));
 			oldSw.disconnect();
 		}
-		
+
 		/*
 		 * Set some other config options for this switch.
 		 */
@@ -480,7 +485,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 	public IOFSwitchBackend getOFSwitchInstance(IOFConnectionBackend connection,
 			SwitchDescription description,
 			OFFactory factory, DatapathId datapathId) {
-		
+
 		return this.driverRegistry.getOFSwitchInstance(connection, description, factory, datapathId);
 	}
 
@@ -488,7 +493,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 	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);
@@ -653,7 +658,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 		driverRegistry = new NaiveSwitchDriverRegistry(this);
 
 		this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
-		
+
 		/* TODO @Ryan
 		try {
 			this.storeClient = this.syncService.getStoreClient(
@@ -685,13 +690,11 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 						)
 				) {
 			log.warn("SSL disabled. Using unsecure connections between Floodlight and switches.");
-			OFSwitchManager.useSsl = false;
 			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.useSsl = true;
 			OFSwitchManager.keyStore = path;
 			OFSwitchManager.keyStorePassword = (pass == null ? "" : pass);
 		}
@@ -762,7 +765,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 			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
@@ -809,7 +812,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 		defaultFactory = computeInitialFactory(ofVersions);
 		ofBitmaps = computeOurVersionBitmaps(ofVersions);
 	}
-	
+
 	/**
 	 * Find the max version supplied in the supported
 	 * versions list and use it as the default, which
@@ -845,7 +848,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 		 */
 		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
@@ -862,7 +865,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 		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 */
@@ -978,45 +981,51 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen
 	 */
 	public void bootstrapNetty() {
 		try {
-			final ServerBootstrap bootstrap = createServerBootStrap();
-
-			bootstrap.setOption("reuseAddr", true);
-			bootstrap.setOption("child.keepAlive", true);
-			bootstrap.setOption("child.tcpNoDelay", true);
-			bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
-			
-			ChannelPipelineFactory pfact = useSsl ? new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, ofBitmaps, defaultFactory, keyStore, keyStorePassword) :
-				new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, ofBitmaps, defaultFactory);
+			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()));
+				}
+			}
 			
-			bootstrap.setPipelineFactory(pfact);
-			InetSocketAddress sa = new InetSocketAddress(floodlightProvider.getOFPort());
-			final ChannelGroup cg = new DefaultChannelGroup();
-			cg.add(bootstrap.bind(sa));
+			for (InetSocketAddress sa : addrs) {
+				cg.add(bootstrap.bind(sa).channel());
+				log.info("Listening for switch connections on {}", sa);
+			}
 
-			log.info("Listening for switch connections on {}", sa);
 		} catch (Exception e) {
 			throw new RuntimeException(e);
 		}
 	}
 
-	/**
-	 * Helper that bootstrapNetty.
-	 * @return
-	 */
-	private ServerBootstrap createServerBootStrap() {
-		if (floodlightProvider.getWorkerThreads() == 0) {
-			return new ServerBootstrap(
-					new NioServerSocketChannelFactory(
-							Executors.newCachedThreadPool(),
-							Executors.newCachedThreadPool()));
-		} else {
-			return new ServerBootstrap(
-					new NioServerSocketChannelFactory(
-							Executors.newCachedThreadPool(),
-							Executors.newCachedThreadPool(), floodlightProvider.getWorkerThreads()));
-		}
-	}
-
 	/**
 	 * Performs startup related actions for logical OF message categories.
 	 * Setting the categories list to immutable ensures that unsupported operation
diff --git a/src/main/java/net/floodlightcontroller/core/util/ThriftFrameDecoder.java b/src/main/java/net/floodlightcontroller/core/util/ThriftFrameDecoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..1dd2d9297c3243fc3d91d09702a99fe6c6796172
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/core/util/ThriftFrameDecoder.java
@@ -0,0 +1,64 @@
+package net.floodlightcontroller.core.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufInputStream;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+
+import org.apache.thrift.TBase;
+import org.apache.thrift.protocol.TCompactProtocol;
+import org.apache.thrift.transport.TIOStreamTransport;
+
+/**
+ * Slice and decode Thrift Serialized Messages from the channel, push a {@link List} of Messages upstream.
+ *
+ * Each message is preceeded by a four-byte length field.
+ *
+ * Subclasses should implement {@link #allocateMessage()} to allocate an instance of the
+ * desired message type.
+ *
+ * Implementation note: this decoder class was initially built for netty3, and is unnecessarily
+ * complex and inelegant. In particular, netty has efficient support for handling lists of messages
+ * now, so this could be replaced by a plain {@link LengthFieldBasedFrameDecoder} to do the slicing
+ * and then a simple ThriftDecoder that would just decode one message at a time.
+ *
+ * @author readams
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ */
+public abstract class ThriftFrameDecoder<T extends TBase<?,?>> extends LengthFieldBasedFrameDecoder {
+    public ThriftFrameDecoder(int maxSize) {
+        super(maxSize, 0, 4, 0, 4);
+    }
+
+    protected abstract T allocateMessage();
+
+    @Override
+    protected final Object decode(ChannelHandlerContext ctx,
+                            ByteBuf buffer) throws Exception {
+        /* This is initialized to null because the decode function must return
+         * null if the buffer does not contain a complete frame and cannot be
+         * decoded.
+         */
+        List<T> ms = null;
+        ByteBuf frame = null;
+        while (null != (frame = (ByteBuf) super.decode(ctx, buffer))) {
+            if (ms == null) ms = new ArrayList<T>();
+            ByteBufInputStream is = new ByteBufInputStream(frame);
+            TCompactProtocol thriftProtocol =
+                    new TCompactProtocol(new TIOStreamTransport(is));
+            T message = allocateMessage();
+            message.read(thriftProtocol);
+            ms.add(message);
+        }
+        return ms;
+    }
+
+    @Override
+    protected final ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index,
+            int length) {
+        return buffer.slice(index, length);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/util/ThriftFrameEncoder.java b/src/main/java/net/floodlightcontroller/core/util/ThriftFrameEncoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..3abd51318477abf6bf132b1d9a7a979d91180493
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/core/util/ThriftFrameEncoder.java
@@ -0,0 +1,45 @@
+package net.floodlightcontroller.core.util;
+
+import org.apache.thrift.TBase;
+import org.apache.thrift.protocol.TCompactProtocol;
+import org.apache.thrift.transport.TIOStreamTransport;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufOutputStream;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
+
+/**
+ * Serialize a single Thrift message into the channel.
+ *
+ * It uses thrift's {@link TCompactProtocol} and frames the message by preprending a four
+ * byte length field (big endian).
+ *
+ * Note: needs to be subclasses with a concrete type implementing T.
+ *
+ * @author readams
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ */
+public abstract class ThriftFrameEncoder<T extends TBase<?,?>> extends MessageToByteEncoder<T> {
+    @Override
+    protected void encode(ChannelHandlerContext ctx, T msg, ByteBuf out)
+            throws Exception {
+
+        int lengthIndex = out.writerIndex();
+        // length field, will be filled in later.
+        out.writeInt(0);
+
+        int startIndex = out.writerIndex();
+        ByteBufOutputStream os = new ByteBufOutputStream(out);
+        TCompactProtocol thriftProtocol =
+                new TCompactProtocol(new TIOStreamTransport(os));
+        msg.write(thriftProtocol);
+        os.close();
+        int endIndex = out.writerIndex();
+
+        // update the length field
+        int length = endIndex - startIndex;
+        out.setInt(lengthIndex, length);
+    }
+}
\ No newline at end of file
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 3481e974505c4bcdfe50801a60a40848796c6ecd..e3981da5fcd74ae4b0abc54b52348c52d3830bdd 100644
--- a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
+++ b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java
@@ -42,6 +42,7 @@ import org.projectfloodlight.openflow.protocol.OFGroupFeaturesStatsReply;
 import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
 import org.projectfloodlight.openflow.protocol.OFMeterBandStats;
+import org.projectfloodlight.openflow.protocol.OFMeterConfig;
 import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply;
 import org.projectfloodlight.openflow.protocol.OFMeterFeatures;
 import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
@@ -409,37 +410,46 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> {
 		jGen.writeStringField("version", meterConfigReply.getVersion().toString()); //return the enum name
 		jGen.writeFieldName("meterConfig");
 		jGen.writeStartArray();
-		for(OFMeterBand band : meterConfigReply.getEntries()) {
+		for (OFMeterConfig config : meterConfigReply.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;
+			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.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;
+				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
+				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();
diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
index 1db7e728d08fa06904b74fb185ec1640b58e159a..19a51d1eb77eb45d8f729447e153a0ddeedb7012 100644
--- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
+++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
@@ -28,6 +28,8 @@ import java.util.Set;
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IOFSwitch;
+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;
@@ -49,11 +51,17 @@ import net.floodlightcontroller.routing.IRoutingService;
 import net.floodlightcontroller.routing.Route;
 import net.floodlightcontroller.topology.ITopologyService;
 import net.floodlightcontroller.topology.NodePortTuple;
+import net.floodlightcontroller.util.FlowModUtils;
+import net.floodlightcontroller.util.OFDPAUtils;
+import net.floodlightcontroller.util.OFPortMode;
+import net.floodlightcontroller.util.OFPortModeTuple;
 
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowModCommand;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
 import org.projectfloodlight.openflow.protocol.OFPacketOut;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.match.Match;
@@ -65,14 +73,16 @@ import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.IpProtocol;
 import org.projectfloodlight.openflow.types.MacAddress;
 import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFGroup;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.TableId;
 import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.VlanVid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class Forwarding extends ForwardingBase implements IFloodlightModule {
+public class Forwarding extends ForwardingBase implements IFloodlightModule, IOFSwitchListener {
 	protected static Logger log = LoggerFactory.getLogger(Forwarding.class);
 
 	@Override
@@ -130,8 +140,9 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 		.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
 		.setBufferId(OFBufferId.NO_BUFFER)
 		.setMatch(m)
-		.setActions(actions) // empty list
 		.setPriority(FLOWMOD_DEFAULT_PRIORITY);
+		
+		FlowModUtils.setActions(fmb, actions, sw);
 
 		try {
 			if (log.isDebugEnabled()) {
@@ -509,5 +520,57 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 	@Override
 	public void startUp(FloodlightModuleContext context) {
 		super.startUp();
+		switchService.addOFSwitchListener(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) {		
+	}
+
+	@Override
+	public void switchChanged(DatapathId switchId) {
 	}
 }
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index a26d8d8fd4a6c39539b2c92c347d0848a3d6ecdd..c50930ed6580cb5cf7a499d83b912522fd2c2821 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -1219,7 +1219,6 @@ IFloodlightModule, IInfoProvider {
 		// send
 		// no more try-catch. switch will silently fail
 		iofSwitch.write(pob.build());
-		iofSwitch.flush();
 	}
 
 	/**
diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
index 4e110e0c8a0453abb608c5c315e16be7846fbd2a..2cf719931d20e71f150e59f254fdedd978aab14e 100644
--- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
+++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
@@ -41,7 +41,9 @@ import net.floodlightcontroller.routing.IRoutingDecision;
 import net.floodlightcontroller.routing.Route;
 import net.floodlightcontroller.topology.ITopologyService;
 import net.floodlightcontroller.topology.NodePortTuple;
+import net.floodlightcontroller.util.FlowModUtils;
 import net.floodlightcontroller.util.MatchUtils;
+import net.floodlightcontroller.util.OFDPAUtils;
 import net.floodlightcontroller.util.OFMessageDamper;
 import net.floodlightcontroller.util.TimedCache;
 
@@ -246,14 +248,15 @@ public abstract class ForwardingBase implements IOFMessageListener {
 			}
 			
 			fmb.setMatch(mb.build())
-			.setActions(actions)
 			.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={} " +
@@ -263,7 +266,18 @@ public abstract class ForwardingBase implements IOFMessageListener {
 							fmb.getMatch().get(MatchField.IN_PORT),
 							outPort });
 				}
-				messageDamper.write(sw, fmb.build());
+				
+				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) &&
@@ -433,8 +447,9 @@ public abstract class ForwardingBase implements IOFMessageListener {
 		.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
 		.setPriority(FLOWMOD_DEFAULT_PRIORITY)
 		.setBufferId(OFBufferId.NO_BUFFER)
-		.setMatch(mb.build())
-		.setActions(actions);
+		.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() });
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
index d4e95df918bb5792906e633c6bf5cab14c9e3780..7875e70267b68508c23d65ad099c50bb66282829 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
+++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java
@@ -610,7 +610,6 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 				log.debug("Sending {} new entries to {}", messages.size(), dpid);
 			}
 			ofswitch.write(messages);
-			ofswitch.flush();
 		}
 	}
 
@@ -626,7 +625,6 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 				log.debug("Sending 1 new entries to {}", dpid.toString());
 			}
 			ofswitch.write(message);
-			ofswitch.flush();
 		}
 	}
 
@@ -654,7 +652,6 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService,
 	 */
 	private void writeFlowModToSwitch(IOFSwitch sw, OFFlowMod flowMod) {
 		sw.write(flowMod);
-		sw.flush();
 	}
 	@Override
 	public String getName() {
diff --git a/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java b/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9a3f462fcc24906bb8fc52f3962b78cc1a161be
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/statistics/IStatisticsService.java
@@ -0,0 +1,18 @@
+package net.floodlightcontroller.statistics;
+
+import java.util.Map;
+
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFPort;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.topology.NodePortTuple;
+
+public interface IStatisticsService extends IFloodlightService {
+
+	public SwitchPortBandwidth getBandwidthConsumption(DatapathId dpid, OFPort p);
+		
+	public Map<NodePortTuple, SwitchPortBandwidth> getBandwidthConsumption();
+	
+	public void collectStatistics(boolean collect);
+}
diff --git a/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java b/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java
new file mode 100644
index 0000000000000000000000000000000000000000..11f33ded0770de03ed3f3d32a1da73ad79e1ac49
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/statistics/StatisticsCollector.java
@@ -0,0 +1,467 @@
+package net.floodlightcontroller.statistics;
+
+import java.lang.Thread.State;
+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 java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsRequest;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+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.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.primitives.UnsignedLong;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import net.floodlightcontroller.core.IOFSwitch;
+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.restserver.IRestApiService;
+import net.floodlightcontroller.statistics.web.SwitchStatisticsWebRoutable;
+import net.floodlightcontroller.threadpool.IThreadPoolService;
+import net.floodlightcontroller.topology.NodePortTuple;
+
+public class StatisticsCollector implements IFloodlightModule, IStatisticsService {
+	private static final Logger log = LoggerFactory.getLogger(StatisticsCollector.class);
+
+	private static IOFSwitchService switchService;
+	private static IThreadPoolService threadPoolService;
+	private static IRestApiService restApiService;
+
+	private static boolean isEnabled = false;
+	
+	private static int portStatsInterval = 10; /* could be set by REST API, so not final */
+	private static ScheduledFuture<?> portStatsCollector;
+
+	private static final long BITS_PER_BYTE = 8;
+	private static final long MILLIS_PER_SEC = 1000;
+	
+	private static final String INTERVAL_PORT_STATS_STR = "collectionIntervalPortStatsSeconds";
+	private static final String ENABLED_STR = "enable";
+
+	private static final HashMap<NodePortTuple, SwitchPortBandwidth> portStats = new HashMap<NodePortTuple, SwitchPortBandwidth>();
+	private static final HashMap<NodePortTuple, SwitchPortBandwidth> tentativePortStats = new HashMap<NodePortTuple, SwitchPortBandwidth>();
+
+	/**
+	 * Run periodically to collect all port statistics. This only collects
+	 * bandwidth stats right now, but it could be expanded to record other
+	 * information as well. The difference between the most recent and the
+	 * current RX/TX bytes is used to determine the "elapsed" bytes. A 
+	 * timestamp is saved each time stats results are saved to compute the
+	 * bits per second over the elapsed time. There isn't a better way to
+	 * compute the precise bandwidth unless the switch were to include a
+	 * timestamp in the stats reply message, which would be nice but isn't
+	 * likely to happen. It would be even better if the switch recorded 
+	 * bandwidth and reported bandwidth directly.
+	 * 
+	 * Stats are not reported unless at least two iterations have occurred
+	 * for a single switch's reply. This must happen to compare the byte 
+	 * counts and to get an elapsed time.
+	 * 
+	 * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
+	 *
+	 */
+	private class PortStatsCollector implements Runnable {
+
+		@Override
+		public void run() {
+			Map<DatapathId, List<OFStatsReply>> replies = getSwitchStatistics(switchService.getAllSwitchDpids(), OFStatsType.PORT);
+			for (Entry<DatapathId, List<OFStatsReply>> e : replies.entrySet()) {
+				for (OFStatsReply r : e.getValue()) {
+					OFPortStatsReply psr = (OFPortStatsReply) r;
+					for (OFPortStatsEntry pse : psr.getEntries()) {
+						NodePortTuple npt = new NodePortTuple(e.getKey(), pse.getPortNo());
+						SwitchPortBandwidth spb;
+						if (portStats.containsKey(npt) || tentativePortStats.containsKey(npt)) {
+							if (portStats.containsKey(npt)) { /* update */
+								spb = portStats.get(npt);
+							} else if (tentativePortStats.containsKey(npt)) { /* finish */
+								spb = tentativePortStats.get(npt);
+								tentativePortStats.remove(npt);
+							} else {
+								log.error("Inconsistent state between tentative and official port stats lists.");
+								return;
+							}
+
+							/* Get counted bytes over the elapsed period. Check for counter overflow. */
+							U64 rxBytesCounted;
+							U64 txBytesCounted;
+							if (spb.getPriorByteValueRx().compareTo(pse.getRxBytes()) > 0) { /* overflow */
+								U64 upper = U64.NO_MASK.subtract(spb.getPriorByteValueRx());
+								U64 lower = pse.getRxBytes();
+								rxBytesCounted = upper.add(lower);
+							} else {
+								rxBytesCounted = pse.getRxBytes().subtract(spb.getPriorByteValueRx());
+							}
+							if (spb.getPriorByteValueTx().compareTo(pse.getTxBytes()) > 0) { /* overflow */
+								U64 upper = U64.NO_MASK.subtract(spb.getPriorByteValueTx());
+								U64 lower = pse.getTxBytes();
+								txBytesCounted = upper.add(lower);
+							} else {
+								txBytesCounted = pse.getTxBytes().subtract(spb.getPriorByteValueTx());
+							}
+							long timeDifSec = (System.currentTimeMillis() - spb.getUpdateTime()) / MILLIS_PER_SEC;
+							portStats.put(npt, SwitchPortBandwidth.of(npt.getNodeId(), npt.getPortId(), 
+									U64.ofRaw((rxBytesCounted.getValue() * BITS_PER_BYTE) / timeDifSec), 
+									U64.ofRaw((txBytesCounted.getValue() * BITS_PER_BYTE) / timeDifSec), 
+									pse.getRxBytes(), pse.getTxBytes())
+									);
+							
+						} else { /* initialize */
+							tentativePortStats.put(npt, SwitchPortBandwidth.of(npt.getNodeId(), npt.getPortId(), U64.ZERO, U64.ZERO, pse.getRxBytes(), pse.getTxBytes()));
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Single thread for collecting switch statistics and
+	 * containing the reply.
+	 * 
+	 * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
+	 *
+	 */
+	private class GetStatisticsThread extends Thread {
+		private List<OFStatsReply> statsReply;
+		private DatapathId switchId;
+		private OFStatsType statType;
+
+		public GetStatisticsThread(DatapathId switchId, OFStatsType statType) {
+			this.switchId = switchId;
+			this.statType = statType;
+			this.statsReply = null;
+		}
+
+		public List<OFStatsReply> getStatisticsReply() {
+			return statsReply;
+		}
+
+		public DatapathId getSwitchId() {
+			return switchId;
+		}
+
+		@Override
+		public void run() {
+			statsReply = getSwitchStatistics(switchId, statType);
+		}
+	}
+	
+	/*
+	 * IFloodlightModule implementation
+	 */
+	
+	@Override
+	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+		Collection<Class<? extends IFloodlightService>> l =
+				new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(IStatisticsService.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(IStatisticsService.class, this);
+		return m;
+	}
+
+	@Override
+	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+		Collection<Class<? extends IFloodlightService>> l =
+				new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(IOFSwitchService.class);
+		l.add(IThreadPoolService.class);
+		l.add(IRestApiService.class);
+		return l;
+	}
+
+	@Override
+	public void init(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+		switchService = context.getServiceImpl(IOFSwitchService.class);
+		threadPoolService = context.getServiceImpl(IThreadPoolService.class);
+		restApiService = context.getServiceImpl(IRestApiService.class);
+
+		Map<String, String> config = context.getConfigParams(this);
+		if (config.containsKey(ENABLED_STR)) {
+			try {
+				isEnabled = Boolean.parseBoolean(config.get(ENABLED_STR).trim());
+			} catch (Exception e) {
+				log.error("Could not parse '{}'. Using default of {}", ENABLED_STR, isEnabled);
+			}
+		}
+		log.info("Statistics collection {}", isEnabled ? "enabled" : "disabled");
+
+		if (config.containsKey(INTERVAL_PORT_STATS_STR)) {
+			try {
+				portStatsInterval = Integer.parseInt(config.get(INTERVAL_PORT_STATS_STR).trim());
+			} catch (Exception e) {
+				log.error("Could not parse '{}'. Using default of {}", INTERVAL_PORT_STATS_STR, portStatsInterval);
+			}
+		}
+		log.info("Port statistics collection interval set to {}s", portStatsInterval);
+	}
+
+	@Override
+	public void startUp(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+		restApiService.addRestletRoutable(new SwitchStatisticsWebRoutable());
+		if (isEnabled) {
+			startStatisticsCollection();
+		}
+	}
+
+	/*
+	 * IStatisticsService implementation
+	 */
+	
+	@Override
+	public SwitchPortBandwidth getBandwidthConsumption(DatapathId dpid, OFPort p) {
+		return portStats.get(new NodePortTuple(dpid, p));
+	}
+	
+
+	@Override
+	public Map<NodePortTuple, SwitchPortBandwidth> getBandwidthConsumption() {
+		return Collections.unmodifiableMap(portStats);
+	}
+	
+	@Override
+	public synchronized void collectStatistics(boolean collect) {
+		if (collect && !isEnabled) {
+			startStatisticsCollection();
+			isEnabled = true;
+		} else if (!collect && isEnabled) {
+			stopStatisticsCollection();
+			isEnabled = false;
+		} 
+		/* otherwise, state is not changing; no-op */
+	}
+	
+	/*
+	 * Helper functions
+	 */
+	
+	/**
+	 * Start all stats threads.
+	 */
+	private void startStatisticsCollection() {
+		portStatsCollector = threadPoolService.getScheduledExecutor().scheduleAtFixedRate(new PortStatsCollector(), portStatsInterval, portStatsInterval, TimeUnit.SECONDS);
+		tentativePortStats.clear(); /* must clear out, otherwise might have huge BW result if present and wait a long time before re-enabling stats */
+		log.warn("Statistics collection thread(s) started");
+	}
+	
+	/**
+	 * Stop all stats threads.
+	 */
+	private void stopStatisticsCollection() {
+		if (!portStatsCollector.cancel(false)) {
+			log.error("Could not cancel port stats thread");
+		} else {
+			log.warn("Statistics collection thread(s) stopped");
+		}
+	}
+
+	/**
+	 * Retrieve the statistics from all switches in parallel.
+	 * @param dpids
+	 * @param statsType
+	 * @return
+	 */
+	private Map<DatapathId, List<OFStatsReply>> getSwitchStatistics(Set<DatapathId> dpids, OFStatsType statsType) {
+		HashMap<DatapathId, List<OFStatsReply>> model = new HashMap<DatapathId, List<OFStatsReply>>();
+
+		List<GetStatisticsThread> activeThreads = new ArrayList<GetStatisticsThread>(dpids.size());
+		List<GetStatisticsThread> pendingRemovalThreads = new ArrayList<GetStatisticsThread>();
+		GetStatisticsThread t;
+		for (DatapathId d : dpids) {
+			t = new GetStatisticsThread(d, statsType);
+			activeThreads.add(t);
+			t.start();
+		}
+
+		/* Join all the threads after the timeout. Set a hard timeout
+		 * of 12 seconds for the threads to finish. If the thread has not
+		 * finished the switch has not replied yet and therefore we won't
+		 * add the switch's stats to the reply.
+		 */
+		for (int iSleepCycles = 0; iSleepCycles < portStatsInterval; iSleepCycles++) {
+			for (GetStatisticsThread curThread : activeThreads) {
+				if (curThread.getState() == State.TERMINATED) {
+					model.put(curThread.getSwitchId(), curThread.getStatisticsReply());
+					pendingRemovalThreads.add(curThread);
+				}
+			}
+
+			/* remove the threads that have completed the queries to the switches */
+			for (GetStatisticsThread curThread : pendingRemovalThreads) {
+				activeThreads.remove(curThread);
+			}
+			
+			/* clear the list so we don't try to double remove them */
+			pendingRemovalThreads.clear();
+
+			/* if we are done finish early */
+			if (activeThreads.isEmpty()) {
+				break;
+			}
+
+			try {
+				Thread.sleep(1000);
+			} catch (InterruptedException e) {
+				log.error("Interrupted while waiting for statistics", e);
+			}
+		}
+
+		return model;
+	}
+
+	/**
+	 * Get statistics from a switch.
+	 * @param switchId
+	 * @param statsType
+	 * @return
+	 */
+	@SuppressWarnings("unchecked")
+	protected List<OFStatsReply> getSwitchStatistics(DatapathId switchId, OFStatsType statsType) {
+		IOFSwitch sw = switchService.getSwitch(switchId);
+		ListenableFuture<?> future;
+		List<OFStatsReply> values = null;
+		Match match;
+		if (sw != null) {
+			OFStatsRequest<?> req = null;
+			switch (statsType) {
+			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:
+				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()
+							.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:		
+			default:
+				log.error("Stats Request Type {} not implemented yet", statsType.name());
+				break;
+			}
+
+			try {
+				if (req != null) {
+					future = sw.writeStatsRequest(req); 
+					values = (List<OFStatsReply>) future.get(portStatsInterval / 2, TimeUnit.SECONDS);
+				}
+			} catch (Exception e) {
+				log.error("Failure retrieving statistics from switch {}. {}", sw, e);
+			}
+		}
+		return values;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/statistics/SwitchPortBandwidth.java b/src/main/java/net/floodlightcontroller/statistics/SwitchPortBandwidth.java
new file mode 100644
index 0000000000000000000000000000000000000000..56648ca0307ccaa5611e660c294684860da049d2
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/statistics/SwitchPortBandwidth.java
@@ -0,0 +1,113 @@
+package net.floodlightcontroller.statistics;
+
+import java.util.Date;
+
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.U64;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import net.floodlightcontroller.statistics.web.SwitchPortBandwidthSerializer;
+
+@JsonSerialize(using=SwitchPortBandwidthSerializer.class)
+public class SwitchPortBandwidth {
+	private DatapathId id;
+	private OFPort pt;
+	private U64 rx;
+	private U64 tx;
+	private Date time;
+	private U64 rxValue;
+	private U64 txValue;
+	
+	private SwitchPortBandwidth() {}
+	private SwitchPortBandwidth(DatapathId d, OFPort p, U64 rx, U64 tx, U64 rxValue, U64 txValue) {
+		id = d;
+		pt = p;
+		this.rx = rx;
+		this.tx = tx;
+		time = new Date();
+		this.rxValue = rxValue;
+		this.txValue = txValue;
+	}
+	
+	public static SwitchPortBandwidth of(DatapathId d, OFPort p, U64 rx, U64 tx, U64 rxValue, U64 txValue) {
+		if (d == null) {
+			throw new IllegalArgumentException("Datapath ID cannot be null");
+		}
+		if (p == null) {
+			throw new IllegalArgumentException("Port cannot be null");
+		}
+		if (rx == null) {
+			throw new IllegalArgumentException("RX bandwidth cannot be null");
+		}
+		if (tx == null) {
+			throw new IllegalArgumentException("TX bandwidth cannot be null");
+		}
+		if (rxValue == null) {
+			throw new IllegalArgumentException("RX value cannot be null");
+		}
+		if (txValue == null) {
+			throw new IllegalArgumentException("TX value cannot be null");
+		}
+		return new SwitchPortBandwidth(d, p, rx, tx, rxValue, txValue);
+	}
+	
+	public DatapathId getSwitchId() {
+		return id;
+	}
+	
+	public OFPort getSwitchPort() {
+		return pt;
+	}
+	
+	public U64 getBitsPerSecondRx() {
+		return rx;
+	}
+	
+	public U64 getBitsPerSecondTx() {
+		return tx;
+	}
+	
+	protected U64 getPriorByteValueRx() {
+		return rxValue;
+	}
+	
+	protected U64 getPriorByteValueTx() {
+		return txValue;
+	}
+	
+	public long getUpdateTime() {
+		return time.getTime();
+	}
+		
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((id == null) ? 0 : id.hashCode());
+		result = prime * result + ((pt == null) ? 0 : pt.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;
+		SwitchPortBandwidth other = (SwitchPortBandwidth) obj;
+		if (id == null) {
+			if (other.id != null)
+				return false;
+		} else if (!id.equals(other.id))
+			return false;
+		if (pt == null) {
+			if (other.pt != null)
+				return false;
+		} else if (!pt.equals(other.pt))
+			return false;
+		return true;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/statistics/web/BandwidthResource.java b/src/main/java/net/floodlightcontroller/statistics/web/BandwidthResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..a09e5f9e0d4d49fce8467f7825f0310eaa981b52
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/statistics/web/BandwidthResource.java
@@ -0,0 +1,75 @@
+package net.floodlightcontroller.statistics.web;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import net.floodlightcontroller.core.internal.IOFSwitchService;
+import net.floodlightcontroller.statistics.IStatisticsService;
+import net.floodlightcontroller.statistics.SwitchPortBandwidth;
+
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BandwidthResource extends ServerResource {
+	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());
+
+		String d = (String) getRequestAttributes().get(SwitchStatisticsWebRoutable.DPID_STR);
+		String p = (String) getRequestAttributes().get(SwitchStatisticsWebRoutable.PORT_STR);
+
+		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 */
+
+		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;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/statistics/web/ConfigResource.java b/src/main/java/net/floodlightcontroller/statistics/web/ConfigResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..881f256c85c042d5e5776dc991f89b9d880cbf2e
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/statistics/web/ConfigResource.java
@@ -0,0 +1,30 @@
+package net.floodlightcontroller.statistics.web;
+
+import java.util.Collections;
+
+import net.floodlightcontroller.statistics.IStatisticsService;
+
+import org.restlet.resource.Post;
+import org.restlet.resource.Put;
+import org.restlet.resource.ServerResource;
+
+public class ConfigResource extends ServerResource {
+
+	@Post
+	@Put
+	public Object config() {
+		IStatisticsService statisticsService = (IStatisticsService) getContext().getAttributes().get(IStatisticsService.class.getCanonicalName());
+
+		if (getReference().getPath().contains(SwitchStatisticsWebRoutable.ENABLE_STR)) {
+			statisticsService.collectStatistics(true);
+			return Collections.singletonMap("statistics-collection", "enabled");
+		}
+		
+		if (getReference().getPath().contains(SwitchStatisticsWebRoutable.DISABLE_STR)) {
+			statisticsService.collectStatistics(false);
+			return Collections.singletonMap("statistics-collection", "disabled");
+		}
+	
+		return Collections.singletonMap("ERROR", "Unimplemented configuration option");
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/statistics/web/SwitchPortBandwidthSerializer.java b/src/main/java/net/floodlightcontroller/statistics/web/SwitchPortBandwidthSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..253bb8c8798c0048a75393ccefb7b983097972f4
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/statistics/web/SwitchPortBandwidthSerializer.java
@@ -0,0 +1,29 @@
+package net.floodlightcontroller.statistics.web;
+
+import java.io.IOException;
+import java.util.Date;
+
+import net.floodlightcontroller.statistics.SwitchPortBandwidth;
+
+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;
+
+public class SwitchPortBandwidthSerializer extends JsonSerializer<SwitchPortBandwidth> {
+
+	@Override
+	public void serialize(SwitchPortBandwidth spb, JsonGenerator jGen, SerializerProvider serializer) throws IOException, JsonProcessingException {
+		jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);
+
+		jGen.writeStartObject();
+		jGen.writeStringField("dpid", spb.getSwitchId().toString());
+		jGen.writeStringField("port", spb.getSwitchPort().toString());
+		jGen.writeStringField("updated", new Date(spb.getUpdateTime()).toString());
+		jGen.writeStringField("bits-per-second-rx", spb.getBitsPerSecondRx().getBigInteger().toString());
+		jGen.writeStringField("bits-per-second-tx", spb.getBitsPerSecondTx().getBigInteger().toString());
+		jGen.writeEndObject();
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/statistics/web/SwitchStatisticsWebRoutable.java b/src/main/java/net/floodlightcontroller/statistics/web/SwitchStatisticsWebRoutable.java
new file mode 100644
index 0000000000000000000000000000000000000000..378d696f5e85504cd9c0a732268f5eda45bf9b79
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/statistics/web/SwitchStatisticsWebRoutable.java
@@ -0,0 +1,31 @@
+package net.floodlightcontroller.statistics.web;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+
+import org.restlet.Context;
+import org.restlet.routing.Router;
+
+public class SwitchStatisticsWebRoutable implements RestletRoutable {
+	protected static final String DPID_STR = "dpid";
+	protected static final String PORT_STR = "port";
+	protected static final String ENABLE_STR = "enable";
+	protected static final String DISABLE_STR = "disable";
+	
+	@Override
+    public Router getRestlet(Context context) {
+        Router router = new Router(context);
+        router.attach("/bandwidth/{" + DPID_STR + "}/{" + PORT_STR + "}/json", BandwidthResource.class);
+        router.attach("/config/enable/json", ConfigResource.class);
+        router.attach("/config/disable/json", ConfigResource.class);
+        return router;
+    }
+
+    /**
+     * Set the base path for the Topology
+     */
+    @Override
+    public String basePath() {
+        return "/wm/statistics";
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/ActionUtils.java b/src/main/java/net/floodlightcontroller/util/ActionUtils.java
index 1f8c8269f19ac092adf2d8dfb526eafff7564031..0620c718f68559c064ebad11d3f635ae5babac19 100644
--- a/src/main/java/net/floodlightcontroller/util/ActionUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/ActionUtils.java
@@ -1179,7 +1179,7 @@ public class ActionUtils {
 		if (n.matches()) {
 			if (n.group(1) != null) {
 				try {
-					TransportPort portnum = TransportPort.of(get_short(n.group(1)));
+					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());
@@ -1213,7 +1213,7 @@ public class ActionUtils {
 		if (n.matches()) {
 			if (n.group(1) != null) {
 				try {
-					TransportPort portnum = TransportPort.of(get_short(n.group(1)));
+					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());
diff --git a/src/main/java/net/floodlightcontroller/util/FlowModUtils.java b/src/main/java/net/floodlightcontroller/util/FlowModUtils.java
index 9a8d6cdbdeaada086daa8f8627da1c426d3f5f1c..8f7645b725bdcc467b6c5e98fce6b3247d7cffe3 100644
--- a/src/main/java/net/floodlightcontroller/util/FlowModUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/FlowModUtils.java
@@ -1,5 +1,10 @@
 package net.floodlightcontroller.util;
 
+import java.util.Collections;
+import java.util.List;
+
+import net.floodlightcontroller.core.IOFSwitch;
+
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFlowAdd;
 import org.projectfloodlight.openflow.protocol.OFFlowDelete;
@@ -8,6 +13,8 @@ import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowModify;
 import org.projectfloodlight.openflow.protocol.OFFlowModifyStrict;
 import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
 
 /**
  * Convert an OFFlowMod to a specific OFFlowMod-OFFlowModCommand.
@@ -221,4 +228,23 @@ public class FlowModUtils {
 					.build();
 		}
 	}
+	
+	/**
+	 * Sets the actions in fmb according to the sw version.
+	 * 
+	 * @param fmb the FlowMod Builder that is being built
+	 * @param actions the actions to set
+	 * @param sw the switch that will receive the FlowMod
+	 */
+	public static void setActions(OFFlowMod.Builder fmb,
+			List<OFAction> actions, IOFSwitch sw) {
+		if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_11) >= 0) {
+			// Instructions are used starting in OF 1.1
+			fmb.setInstructions(Collections.singletonList((OFInstruction) sw
+					.getOFFactory().instructions().applyActions(actions)));
+		} else {
+			// OF 1.0 only supports actions
+			fmb.setActions(actions);
+		}
+	}
 }
diff --git a/src/main/java/net/floodlightcontroller/util/IterableUtils.java b/src/main/java/net/floodlightcontroller/util/IterableUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..d94d81160641de3c046741c9073245ce35552ff1
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/util/IterableUtils.java
@@ -0,0 +1,28 @@
+package net.floodlightcontroller.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Because it's handy.
+ * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
+ */
+public class IterableUtils {
+	
+	/**
+	 * Convert an Iterable to a Collection (ArrayList under the hood). All items in the
+	 * Iterable will be retained.
+	 * @param i
+	 * @return
+	 */
+	public static <T> Collection<T> toCollection(Iterable<T> i) {
+		if (i == null) {
+			throw new IllegalArgumentException("Iterable 'i' cannot be null");
+		}
+		Collection<T> c = new ArrayList<T>();
+		for (T t : i) {
+			c.add(t);
+		}
+		return c;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java
index f2744985b46813c045828174c8142dab3f418e54..b3a7904125066496f6b282bf24fe66d9d0a76801 100644
--- a/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java
+++ b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java
@@ -71,42 +71,41 @@ public class OFDPAUtils {
 	public static final int DLF_PRIORITY = 0;
 	public static final int HARD_TIMEOUT = 0;
 	public static final int IDLE_TIMEOUT = 0;
-	private static final long APP_COOKIE = 1000;
-	public static final U64 COOKIE = U64.of(APP_COOKIE);
+	public static final U64 APP_COOKIE = U64.of(Long.parseLong("00FFDDBBAA", 16)); /* OF-DPA sub B for P :-) */
 
 	private static class OFDPAGroupType {
-		static final int L2_INTERFACE = 0;			/* 0 */
-		static final int L2_REWRITE = 1; 			/* 1 */
-		static final int L3_UNICAST = 2;				/* 2 */
-		static final int L2_MULTICAST = 3; 			/* 3 */
-		static final int L2_FLOOD = 4; 				/* 4 */
-		static final int L3_INTERFACE = 5; 			/* 5 */
-		static final int L3_MULTICAST = 6;			/* 6 */
-		static final int L3_ECMP = 7;				/* 7 */
-		static final int L2_DATA_CENTER_OVERLAY = 8; /* 8 */
-		static final int MPLS_LABEL = 9; 			/* 9 */
-		static final int MPLS_FORWARDING = 10;		/* 10 */
-		static final int L2_UNFILTERED_INTERFACE = 11; /* 11 */
-		static final int L2_LOOPBACK = 12;   		/* 12 */
+		private static final int L2_INTERFACE = 0;				/* 0 */
+		private static final int L2_REWRITE = 1; 				/* 1 */
+		private static final int L3_UNICAST = 2;				/* 2 */
+		private static final int L2_MULTICAST = 3; 				/* 3 */
+		private static final int L2_FLOOD = 4; 					/* 4 */
+		private static final int L3_INTERFACE = 5; 				/* 5 */
+		private static final int L3_MULTICAST = 6;				/* 6 */
+		private static final int L3_ECMP = 7;					/* 7 */
+		private static final int L2_DATA_CENTER_OVERLAY = 8; 	/* 8 */
+		private static final int MPLS_LABEL = 9; 				/* 9 */
+		private static final int MPLS_FORWARDING = 10;			/* 10 */
+		private static final int L2_UNFILTERED_INTERFACE = 11;	/* 11 */
+		private static final int L2_LOOPBACK = 12;   			/* 12 */
 	}
 
 	private static class L2OverlaySubType {
-		static final int L2_OVERLAY_FLOOD_OVER_UNICAST_TUNNELS = 0;
-		static final int L2_OVERLAY_FLOOD_OVER_MULTICAST_TUNNELS = 1;
-		static final int L2_OVERLAY_MULTICAST_OVER_UNICAST_TUNNELS = 2;
-		static final int L2_OVERLAY_MULTICAST_OVER_MULTICAST_TUNNELS = 3;
+		private static final int L2_OVERLAY_FLOOD_OVER_UNICAST_TUNNELS = 0;
+		private static final int L2_OVERLAY_FLOOD_OVER_MULTICAST_TUNNELS = 1;
+		private static final int L2_OVERLAY_MULTICAST_OVER_UNICAST_TUNNELS = 2;
+		private static final int L2_OVERLAY_MULTICAST_OVER_MULTICAST_TUNNELS = 3;
 	}
 
 	private static class MPLSSubType {
-		static final int MPLS_INTERFACE = 0;
-		static final int MPLS_L2_VPN_LABEL = 1;
-		static final int MPLS_L3_VPN_LABEL = 2;
-		static final int MPLS_TUNNEL_LABEL_1 = 3;
-		static final int MPLS_TUNNEL_LABEL_2 = 4;
-		static final int MPLS_SWAP_LABEL = 5;
-		static final int MPLS_FAST_FAILOVER = 6;
-		static final int MPLS_ECMP = 8;
-		static final int MPLS_L2_TAG = 10;
+		private static final int MPLS_INTERFACE = 0;
+		private static final int MPLS_L2_VPN_LABEL = 1;
+		private static final int MPLS_L3_VPN_LABEL = 2;
+		private static final int MPLS_TUNNEL_LABEL_1 = 3;
+		private static final int MPLS_TUNNEL_LABEL_2 = 4;
+		private static final int MPLS_SWAP_LABEL = 5;
+		private static final int MPLS_FAST_FAILOVER = 6;
+		private static final int MPLS_ECMP = 8;
+		private static final int MPLS_L2_TAG = 10;
 	}
 
 	public static class Tables {
@@ -209,17 +208,19 @@ public class OFDPAUtils {
 	}
 
 	/**
-	 * Add the OFDPA groups and flows necessary to facilitate future forwarding/learning 
+	 * Add the OF-DPA groups and flows necessary to facilitate future forwarding/learning 
 	 * switch flows. The switch provided must be an OFDPA 2.0 compliant switch.
 	 * Use VLAN tag of null or VlanVid.ZERO for untagged. VLAN tag 1 may not be used; it is 
-	 * reserved as an internal VLAN.
+	 * reserved as an internal VLAN for untagged ports on no VLAN (like a home switch).
 	 * 
 	 * This function will add the flows that permit all packets in the VLAN specified and
-	 * on the ports specified to reach the policy ACL table. The policy ACL table (60) of
-	 * the OF-DPA switch will contain a DLF, zero-priority flow to forward all packets to
-	 * the controller for processing. A packet forwarded to the controller from the policy
-	 * ACL table can be either handled manually or have a flow inserted for it via 
-	 * {@link OFDPAUtils#addBridgingFlow(IOFSwitch, U64, int, int, int, Match, VlanVid, OFPort) this function}.
+	 * on the ports specified to reach the bridging table, where a DLF flow will forward 
+	 * them to the controller if another higher priority L2 flow does not match in the
+	 * bridging table. All ethertypes will match this DLF flow.
+	 * 
+	 * Use {@link OFDPAUtils#addLearningSwitchFlow(IOFSwitch, U64, int, int, int, Match, VlanVid, OFPort) this function }
+	 * to insert learning switch flows at a later point based on packets forwarded to the
+	 * controller from the DLF flow.
 	 * 
 	 * @param sw
 	 * @param vlan
@@ -228,11 +229,9 @@ public class OFDPAUtils {
 	 */
 	public static boolean addLearningSwitchPrereqs(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
 		/*
-		 * Both of these must complete. If the first fails, the second will not be executed. (AND short-circuit)
-		 * 
-		 * Groups must be added last, since they require the VLANs to be added first.
+		 * Both of these must complete. If the first fails, the second will not be executed.
 		 */
-		return addLearningSwitchPrereqFlows(sw, vlan, ports) && addLearningSwitchPrereqGroups(sw, vlan, ports) ;
+		return addLearningSwitchPrereqGroups(sw, vlan, ports) && addLearningSwitchPrereqFlows(sw, vlan, ports);
 	}
 
 	/**
@@ -251,7 +250,7 @@ public class OFDPAUtils {
 	 * @param ports
 	 * @return
 	 */
-	public static boolean addLearningSwitchPrereqGroups(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
+	private static boolean addLearningSwitchPrereqGroups(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
 		if (sw == null) {
 			throw new NullPointerException("Switch cannot be null.");
 		}
@@ -269,7 +268,7 @@ public class OFDPAUtils {
 			for (OFPortModeTuple p : ports) {
 				if (sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getShortPortNumber() > 0xFF00)) {
 					throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
-				} else if (!sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getPortNumber() > 0xffFFff00)) {
+				} else if (!sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || U32.of(p.getPort().getPortNumber()).compareTo(U32.of(0xffFFff00)) != -1)) {
 					throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
 				}
 			}
@@ -286,7 +285,7 @@ public class OFDPAUtils {
 			actions.add(sw.getOFFactory().actions().output(p.getPort(), 0xffFFffFF));
 
 			OFGroupAdd ga = sw.getOFFactory().buildGroupAdd()
-					.setGroup(GroupIds.createL2Interface(p.getPort(), (vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)))
+					.setGroup(GroupIds.createL2Interface(p.getPort(), vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))
 					.setGroupType(OFGroupType.INDIRECT)
 					.setBuckets(Collections.singletonList(
 							sw.getOFFactory().buildBucket()
@@ -303,15 +302,12 @@ public class OFDPAUtils {
 		List<OFBucket> bucketList = new ArrayList<OFBucket>(ports.size());
 		for (OFPortModeTuple p : ports) {
 			List<OFAction> actions = new ArrayList<OFAction>();
-			if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) { /* ditto */
-				actions.add(sw.getOFFactory().actions().popVlan());
-			}
-			actions.add(sw.getOFFactory().actions().output(p.getPort(), 0xffFFffFF));
+			actions.add(sw.getOFFactory().actions().group(GroupIds.createL2Interface(p.getPort(), vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)));
 			bucketList.add(sw.getOFFactory().buildBucket().setActions(actions).build());
 		}
 		OFGroupAdd ga = sw.getOFFactory().buildGroupAdd() /* use the VLAN ID as the group ID */
-				.setGroup(GroupIds.createL2Flood(U16.of(vlan.getVlan()), vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))
-				.setGroupType(OFGroupType.INDIRECT)
+				.setGroup(GroupIds.createL2Flood(U16.of((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan).getVlan()), vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))
+				.setGroupType(OFGroupType.ALL)
 				.setBuckets(bucketList)
 				.build();
 		sw.write(ga);
@@ -335,7 +331,7 @@ public class OFDPAUtils {
 	 * @param ports
 	 * @return
 	 */
-	public static boolean addLearningSwitchPrereqFlows(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
+	private static boolean addLearningSwitchPrereqFlows(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) {
 		if (sw == null) {
 			throw new NullPointerException("Switch cannot be null.");
 		}
@@ -353,7 +349,7 @@ public class OFDPAUtils {
 			for (OFPortModeTuple p : ports) {
 				if (sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getShortPortNumber() > 0xFF00)) {
 					throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
-				} else if (!sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getPortNumber() > 0xffFFff00)) {
+				} else if (!sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || U32.of(p.getPort().getPortNumber()).compareTo(U32.of(0xffFFff00)) != -1)) {
 					throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString());
 				}
 			}
@@ -368,72 +364,103 @@ public class OFDPAUtils {
 		 * default tag of 1. Only VLANs are handled in this table.
 		 */
 		ArrayList<OFInstruction> instructions = new ArrayList<OFInstruction>();
-		ArrayList<OFAction> actions = new ArrayList<OFAction>();
+		ArrayList<OFAction> applyActions = new ArrayList<OFAction>();
+		ArrayList<OFAction> writeActions = new ArrayList<OFAction>();
 		Match.Builder mb = sw.getOFFactory().buildMatch();
 		OFFlowAdd.Builder fab = sw.getOFFactory().buildFlowAdd();
 
 		/* These are common to all flows for VLAN flow table. */
 		fab.setBufferId(OFBufferId.NO_BUFFER)
-		.setCookie(COOKIE)
+		.setCookie(APP_COOKIE)
 		.setHardTimeout(HARD_TIMEOUT)
 		.setIdleTimeout(IDLE_TIMEOUT)
 		.setPriority(PRIORITY)
 		.setTableId(Tables.VLAN);
 
+		/*
+		 * For each port, if it's an access port, must first push flow for tagged,
+		 * THEN push flow for untagged. If it's trunk and access, do the same.
+		 * If it's just trunk, then only push the tagged flow.
+		 */
 		for (OFPortModeTuple p : ports) {
-			/* If VLAN tag not present add a tag (internal=1 or defined) */
-			if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) { /* push tag if access or untagged entirely */
-				mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.UNTAGGED);
-				// happens automatically actions.add(sw.getOFFactory().actions().buildPushVlan().setEthertype(EthType.VLAN_FRAME).build());
-				actions.add(sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)))));
-				instructions.add(sw.getOFFactory().instructions().applyActions(actions));
-			} else {
-				mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)));
-			}
+			/* OF-DPA requires match on VLAN tag for both trunk and access ports */
+			mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)));
 
 			/* No matter what, we need to match on the ingress port */
 			mb.setExact(MatchField.IN_PORT, p.getPort());
 
 			/* We have to do this for OF-DPA. It's like adding the VLAN to the switch on that port. */
-			instructions.add(sw.getOFFactory().instructions().applyActions(Collections.singletonList((OFAction) 
-					sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))))
-					)));
+			/* Happens automatically: actions.add(sw.getOFFactory().actions().buildPushVlan().setEthertype(EthType.VLAN_FRAME).build()); */
+			applyActions.add(sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)))));
+			instructions.add(sw.getOFFactory().instructions().applyActions(applyActions));
 			/* No matter what, output to the next table */
 			instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.TERMINATION_MAC));
 
-			/* Set the new stuff. */
 			fab.setInstructions(instructions)
 			.setMatch(mb.build())
 			.build();
 			sw.write(fab.build());
-			log.debug("Writing prereq flow to VLAN flow table {}", fab.build().toString());
+			if (log.isDebugEnabled()) {
+				log.debug("Writing tagged prereq flow to VLAN flow table {}", fab.build().toString());
+			}
 
 			/* Don't forget to empty out our containers for the next iteration (or below). */
 			instructions.clear();
-			actions.clear();
+			applyActions.clear();
 			mb = sw.getOFFactory().buildMatch();
+
+			/* Here, if the port is access, add another untagged flow */
+			if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) {
+				mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.UNTAGGED); //TODO verify this
+				mb.setExact(MatchField.IN_PORT, p.getPort());
+
+				applyActions.add(sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)))));
+				instructions.add(sw.getOFFactory().instructions().applyActions(applyActions));
+				instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.TERMINATION_MAC));
+
+				fab.setInstructions(instructions)
+				.setMatch(mb.build())
+				.build();
+				sw.write(fab.build());
+				if (log.isDebugEnabled()) {
+					log.debug("Writing untagged prereq flow to VLAN flow table {}", fab.build().toString());
+				}
+
+				/* Don't forget to empty out our containers for the next iteration (or below). */
+				instructions.clear();
+				applyActions.clear();
+				mb = sw.getOFFactory().buildMatch();
+			}
 		}
 
+		/* Termination MAC table auto-forwards to bridging table (50) */
+
 		/*
-		 * We will insert a DLF flow to send to controller in the Policy ACL table (60).
-		 * TODO Maybe this isn't the best choice, since we assume bypass/auto-forwarding of bridging and unicast/mulicast routing tables.
+		 * We will insert a DLF flow to send to controller in the bridging table (50).
 		 */
-		actions.add(sw.getOFFactory().actions().output(OFPort.CONTROLLER, 0xffFFffFF));
-		instructions.add(sw.getOFFactory().instructions().applyActions(actions));
-		fab = fab.setMatch(sw.getOFFactory().buildMatch().build()) /* clear match */
+		writeActions.add(sw.getOFFactory().actions().group(OFDPAUtils.GroupIds.createL2Flood(
+				U16.of((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan).getVlan()) /* ID */, 
+				vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))); /* bogus action */
+		applyActions.add(sw.getOFFactory().actions().output(OFPort.CONTROLLER, 0xffFFffFF)); /* real, intended action */
+		instructions.add(sw.getOFFactory().instructions().writeActions(writeActions));
+		instructions.add(sw.getOFFactory().instructions().applyActions(applyActions));
+		instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.POLICY_ACL)); /* must go to policy ACL otherwise dropped; bogus though */
+		fab = fab.setMatch(sw.getOFFactory().buildMatch()
+				.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid(vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)) /* must match on just VLAN; dst MAC wildcarded */
+				.build())
 				.setInstructions(instructions)
-				.setPriority(DLF_PRIORITY) /* different zero priority and table ID here */
-				.setTableId(Tables.POLICY_ACL);
+				.setPriority(DLF_PRIORITY) /* lower priority */
+				.setTableId(Tables.BRIDGING);
 		sw.write(fab.build());
-		log.debug("Writing DLF flow to policy ACL table {}", fab.build().toString());
+		if (log.isDebugEnabled()) {
+			log.debug("Writing DLF flow to bridging table {}", fab.build().toString());
+		}
 
 		return true;
 	}
 
 	/**
-	 * Note: Must have individually added {@link OFDPAUtils#addLearningSwitchPrereqGroups(IOFSwitch, VlanVid, List) groups} 
-	 * and then {@link OFDPAUtils#addLearningSwitchPrereqFlows(IOFSwitch, VlanVid, List) flows},
-	 * or must have done {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) both} prior to calling 
+	 * Note: Must have called {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) this function } prior to calling 
 	 * this function. It is assumed you have done the aforementioned with the same VLAN and ports, otherwise you will likely
 	 * get a very grumpy OF-DPA switch.
 	 * 
@@ -463,7 +490,7 @@ public class OFDPAUtils {
 	 * @param outPort, either a valid physical port number or ZERO (for drop), ALL, FLOOD, or CONTROLLER
 	 * @return true upon success; false if switch is not an OF-DPA switch
 	 */
-	public static boolean addBridgingFlow(IOFSwitch sw, U64 cookie, int priority, int hardTimeout, int idleTimeout, Match match, VlanVid outVlan, OFPort outPort) {
+	public static boolean addLearningSwitchFlow(IOFSwitch sw, U64 cookie, int priority, int hardTimeout, int idleTimeout, Match match, VlanVid outVlan, OFPort outPort) {
 		if (!isOFDPASwitch(sw)) {
 			log.error("Switch {} is not an OF-DPA switch. Not inserting flows.", sw.getId().toString());
 			return false;
@@ -477,7 +504,10 @@ public class OFDPAUtils {
 		hardTimeout = (hardTimeout < 0 ? 0 : hardTimeout);
 		idleTimeout = (idleTimeout < 0 ? 0 : idleTimeout);
 		if (match == null || !match.isExact(MatchField.ETH_DST)) {
-			log.error("OF-DPA 2.0 requires at least the destination MAC be matched in order to forward through its pipeline.");
+			log.error("OF-DPA 2.0 requires the destination MAC be matched in order to forward through its pipeline.");
+			return false;
+		} else if (match == null || !match.isExact(MatchField.VLAN_VID)) {
+			log.error("OF-DPA 2.0 requires the VLAN be matched in order to forward through its pipeline.");
 			return false;
 		} else {
 			List<MatchFields> mfs = checkMatchFields(match);
@@ -490,85 +520,49 @@ public class OFDPAUtils {
 		outPort = (outPort == null ? OFPort.ZERO : outPort);
 
 		/*
-		 * Ingress flow table (0) will automatically send to the
-		 * VLAN flow table (10), so insert nothing here.
-		 */
-
-		/*
-		 * VLAN flow table (10) is handled by prereq flows.
-		 */
-
-		/*
-		 * Termination MAC table (20) will automatically send to the
-		 * bridging flow table (50), so also insert nothing here.
-		 * 
-		 * Can send to controller.
+		 * Add flow to bridging table that matches on dst MAC and outputs
+		 * to the known port where the next hop or destination resides.
 		 */
+		ArrayList<OFInstruction> instructions = new ArrayList<OFInstruction>();
+		ArrayList<OFAction> actions = new ArrayList<OFAction>();
+		
+		actions.add(sw.getOFFactory().actions().group(GroupIds.createL2Interface(outPort, (outVlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : outVlan))));
+		instructions.add(sw.getOFFactory().instructions().writeActions(actions));
+		instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.POLICY_ACL)); /* must go here or dropped */
 
-		/*
-		 * Unicast routing (30) and multicast routing (40) flow tables
-		 * are special use-case tables the application should program 
-		 * directly. As such, we won't consider them here.
-		 * 
-		 * Can send to controller.
-		 */
+		OFFlowAdd fa = sw.getOFFactory().buildFlowAdd()
+				.setMatch(sw.getOFFactory().buildMatch()
+						.setExact(MatchField.VLAN_VID, match.get(MatchField.VLAN_VID))
+						.setExact(MatchField.ETH_DST, match.get(MatchField.ETH_DST))
+						.build())
+						.setPriority(priority)
+						.setIdleTimeout(idleTimeout)
+						.setHardTimeout(hardTimeout)
+						.setBufferId(OFBufferId.NO_BUFFER)
+						.setCookie(OFDPAUtils.APP_COOKIE)
+						.setTableId(OFDPAUtils.Tables.BRIDGING)
+						.setInstructions(instructions)
+						.build();
+		log.debug("Writing learning switch flow to bridging table: {}", fa);
+		sw.write(fa);
 
 		/*
-		 * Bridging table (50) should assign a write-action goto-group 
-		 * depending on the desired output action (single-port or 
-		 * flood). But, the default on miss is to go to the policy ACL
-		 * table (60), which we will do. Policy ACL can also assign the group.
-		 * which we will do. Bridging must match on the VLAN VID and the
-		 * dest MAC address of the packet. It must have a priority greater
-		 * than all less-specific flows in the table (i.e. wildcarded
-		 * flows). We will reserve priority 0 for a DLF (destination
-		 * lookup failure) flow, which would have all fields wildcarded.
-		 * 
-		 * Can send to controller.
+		 * Policy ACL table (60) allows for more detailed matches. The 
+		 * write-actions goto group inserted by the bridging table (50) 
+		 * will be the output action taken upon a match. The policy ACL
+		 * table allows for (optional) additional matches to take place.
+		 * If we want to drop a packet instead of forwarding it, then 
+		 * the output group must be cleared in a flow that matches on
+		 * the destination MAC and VLAN (same as bridging).
 		 */
 
-		/*
-		 * Policy ACL table (60) allows for more detailed matches. This
-		 * is where we will implement all the matches specified in the Match
-		 * object. The write-actions goto group inserted by the bridging 
-		 * table (50), or here (60), will be the output action taken upon 
-		 * a match, since this is the end of the pipeline. If we want to 
-		 * drop a packet for not matching, then no output group will be
-		 * assigned to the packet, thus dropping it.
-		 * 
-		 * A DLF (destination lookup failure) flow can also be inserted
-		 * here to forward packets to the controller.
+		/* 
+		 * For now, let's assume we only match on VLAN and destination MAC.
+		 * If that's the case, we should be able to do L2 forwarding b/t
+		 * ports residing on the same VLAN.
 		 * 
-		 * Can send to controller.
-		 */
-
-		ArrayList<OFInstruction> instructions = new ArrayList<OFInstruction>();
-		ArrayList<OFAction> actions = new ArrayList<OFAction>();
-
-		/* Set the group to which we want to output. This might be a L2 flood or interface. */
-		if (outPort.equals(OFPort.ZERO)) {
-			/* Don't add a group at all --> DROP */
-		} else if (outPort.equals(OFPort.FLOOD) || outPort.equals(OFPort.ALL)) { // TODO how to distinguish in OF-DPA?
-			actions.add(
-					sw.getOFFactory().actions().group( // TODO Assume there is only one flood group per VLAN
-							GroupIds.createL2Flood(U16.ZERO, (outVlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : outVlan))
-							)
-					);
-			instructions.add(sw.getOFFactory().instructions().writeActions(actions));
-		} else if (outPort.equals(OFPort.CONTROLLER)) {
-			actions.add(sw.getOFFactory().actions().output(OFPort.CONTROLLER, 0xFFffFFff));
-			instructions.add(sw.getOFFactory().instructions().applyActions(actions));
-		} else { /* assume port is a number valid on the switch */
-			actions.add(
-					sw.getOFFactory().actions().group(
-							GroupIds.createL2Interface(outPort, (outVlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : outVlan))
-							)
-					);
-			instructions.add(sw.getOFFactory().instructions().writeActions(actions));
-		}
-
-		/* We're allowed to match on anything in the Match (supplied as an argument to this function) at this point. */
-		OFFlowAdd fa = sw.getOFFactory().buildFlowAdd()
+		 * We're allowed to match on anything in the Match (supplied as an argument to this function) at this point. 
+		fa = sw.getOFFactory().buildFlowAdd()
 				.setBufferId(OFBufferId.NO_BUFFER)
 				.setCookie(cookie)
 				.setHardTimeout(hardTimeout)
@@ -578,7 +572,8 @@ public class OFDPAUtils {
 				.setMatch(match)
 				.setInstructions(instructions)
 				.build();
-		sw.write(fa);
+		log.debug("Writing learning switch flow to policy ACL table: {}", fa);
+		sw.write(fa); */
 
 		return true;
 	}
diff --git a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
index 296adc157236de7dac02f5f15d544abc66228450..3e1b86f59362d077e513705b76da1c235f956b7b 100644
--- a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
+++ b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
@@ -114,24 +114,8 @@ public class OFMessageDamper {
      * @throws IOException
      */
     public boolean write(IOFSwitch sw, OFMessage msg) throws IOException {
-        return write(sw, msg, false);
-    }
-    
-    /**
-     * write the message to the switch according to our dampening settings
-     * @param sw
-     * @param msg
-     * @param flush true to flush the packet immediately
-     * @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, boolean flush) throws IOException {
         if (!msgTypesToCache.contains(msg.getType())) {
             sw.write(msg);
-            if (flush) {
-                sw.flush();
-            }
             return true;
         }
         
@@ -141,9 +125,6 @@ public class OFMessageDamper {
             return false; 
         } else {
             sw.write(msg);
-            if (flush) {
-                sw.flush();
-            }
             return true;
         }
     }
diff --git a/src/main/java/net/floodlightcontroller/util/OFPortMode.java b/src/main/java/net/floodlightcontroller/util/OFPortMode.java
index 9a02a47fb93a3f54c5960ae5af93490d2a85d2bc..e036cffa61049c2e925812952c6dd573c007a404 100644
--- a/src/main/java/net/floodlightcontroller/util/OFPortMode.java
+++ b/src/main/java/net/floodlightcontroller/util/OFPortMode.java
@@ -2,13 +2,12 @@ package net.floodlightcontroller.util;
 
 /**
  * Define the different operating modes of a switch
- * port as trunk, access, and both (trunk and access).
+ * port as trunk or access.
  * 
  * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu
  */
 
 public enum OFPortMode {
 	TRUNK,
-	ACCESS,
-	TRUNK_AND_ACCESS
+	ACCESS
 }
diff --git a/src/main/java/org/sdnplatform/sync/internal/SyncManager.java b/src/main/java/org/sdnplatform/sync/internal/SyncManager.java
index 9db240c9c665b3da816a1b3f8803d1fd49914dc2..540fa42b1c1155ee49869caf8403c937e4d83052 100644
--- a/src/main/java/org/sdnplatform/sync/internal/SyncManager.java
+++ b/src/main/java/org/sdnplatform/sync/internal/SyncManager.java
@@ -1,5 +1,8 @@
 package org.sdnplatform.sync.internal;
 
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -68,737 +71,745 @@ import net.floodlightcontroller.threadpool.IThreadPoolService;
  * @see ISyncService
  */
 public class SyncManager extends AbstractSyncManager {
-    protected static final Logger logger =
-            LoggerFactory.getLogger(SyncManager.class.getName());
-
-    protected IThreadPoolService threadPool;
-    protected IDebugCounterService debugCounter;
-
-    /**
-     * The store registry holds the storage engines that provide
-     * access to the data
-     */
-    private StoreRegistry storeRegistry = null;
-
-    private IClusterConfigProvider clusterConfigProvider;
-    private ClusterConfig clusterConfig = new ClusterConfig();
-
-    protected RPCService rpcService = null;
-
-    /**
-     * Interval between cleanup tasks in seconds
-     */
-    private static final int CLEANUP_INTERVAL = 60 * 60;
-
-    /**
-     * Interval between antientropy tasks in seconds
-     */
-    private static final int ANTIENTROPY_INTERVAL = 5 * 60;
-
-    /**
-     * Interval between configuration rescans
-     */
-    private static final int CONFIG_RESCAN_INTERVAL = 10;
-
-    /**
-     * Task for performing periodic maintenance/cleanup on local stores
-     */
-    private SingletonTask cleanupTask;
-
-    /**
-     * Task for periodic antientropy between nodes
-     */
-    private SingletonTask antientropyTask;
-
-    /**
-     * Task to periodically rescan configuration
-     */
-    private SingletonTask updateConfigTask;
-
-    /**
-     * Number of {@link HintWorker} workers used to drain the queue of writes
-     * that need to be sent to the connected nodes
-     */
-    private static final int SYNC_WORKER_POOL = 2;
-
-    /**
-     * A thread pool for the {@link HintWorker} threads.
-     */
-    private ExecutorService hintThreadPool;
-
-    /**
-     * Random number generator
-     */
-    private final Random random = new Random();
-
-    /**
-     * A map of the currently-allocated cursors
-     */
-    private final Map<Integer, Cursor> cursorMap =
-            new ConcurrentHashMap<Integer, Cursor>();
-
-    /**
-     * Whether to allow persistent stores or to use in-memory even
-     * when persistence is requested
-     */
-    private boolean persistenceEnabled = true;
-
-    private static final String PACKAGE =
-            ISyncService.class.getPackage().getName();
-
-    /**
-     * Debug Counters
-     */
-    public static IDebugCounter counterHints;
-    public static IDebugCounter counterSentValues;
-    public static IDebugCounter counterReceivedValues;
-    public static IDebugCounter counterPuts;
-    public static IDebugCounter counterGets;
-    public static IDebugCounter counterIterators;
-    public static IDebugCounter counterErrorRemote;
-    public static IDebugCounter counterErrorProcessing;
-
-    // ************
-    // ISyncService
-    // ************
-
-    @Override
-    public void registerStore(String storeName, Scope scope) {
-        try {
-            storeRegistry.register(storeName, scope, false);
-        } catch (PersistException e) {
-            // not possible
-            throw new SyncRuntimeException(e);
-        }
-    }
-
-    @Override
-    public void registerPersistentStore(String storeName, Scope scope)
-            throws PersistException {
-        storeRegistry.register(storeName, scope, persistenceEnabled);
-    }
-
-    // **************************
-    // SyncManager public methods
-    // **************************
-
-    /**
-     * Get the cluster configuration object
-     * @return the {@link ClusterConfig} object
-     * @see ClusterConfig
-     */
-    public ClusterConfig getClusterConfig() {
-        return clusterConfig;
-    }
-
-    /**
-     * Perform periodic scheduled cleanup.  Note that this will be called
-     * automatically and you shouldn't generally call it directly except for
-     * testing
-     * @throws SyncException
-     */
-    public void cleanup() throws SyncException {
-        for (SynchronizingStorageEngine store : storeRegistry.values()) {
-            store.cleanupTask();
-        }
-    }
-
-    /**
-     * Perform a synchronization with the node specified
-     */
-    public void antientropy(Node node) {
-        if (!rpcService.isConnected(node.getNodeId())) return;
-
-        logger.info("[{}->{}] Synchronizing local state to remote node",
-                    getLocalNodeId(), node.getNodeId());
-
-        for (SynchronizingStorageEngine store : storeRegistry.values()) {
-            if (Scope.LOCAL.equals(store.getScope())) {
-                if (node.getDomainId() !=
-                        getClusterConfig().getNode().getDomainId())
-                    continue;
-            } else if (Scope.UNSYNCHRONIZED.equals(store.getScope())) {
-                continue;
-            }
-
-            IClosableIterator<Entry<ByteArray,
-                                  List<Versioned<byte[]>>>> entries =
-                    store.entries();
-            try {
-                SyncMessage bsm =
-                        TProtocolUtil.getTSyncOfferMessage(store.getName(),
-                                                           store.getScope(),
-                                                           store.isPersistent());
-                int count = 0;
-                while (entries.hasNext()) {
-                    if (!rpcService.isConnected(node.getNodeId())) return;
-
-                    Entry<ByteArray, List<Versioned<byte[]>>> pair =
-                            entries.next();
-                    KeyedVersions kv =
-                            TProtocolUtil.getTKeyedVersions(pair.getKey(),
-                                                            pair.getValue());
-                    bsm.getSyncOffer().addToVersions(kv);
-                    count += 1;
-                    if (count >= 50) {
-                        sendSyncOffer(node.getNodeId(), bsm);
-                        bsm.getSyncOffer().unsetVersions();
-                        count = 0;
-                    }
-                }
-                sendSyncOffer(node.getNodeId(), bsm);
-            } catch (InterruptedException e) {
-                // This can't really happen
-                throw new RuntimeException(e);
-            } finally {
-                entries.close();
-            }
-        }
-    }
-
-    /**
-     * Communicate with a random node and do a full synchronization of the
-     * all the stores on each node that have the appropriate scope.
-     */
-    public void antientropy() {
-        ArrayList<Node> candidates = new ArrayList<Node>();
-        for (Node n : clusterConfig.getNodes())
-            if (rpcService.isConnected(n.getNodeId()))
-                candidates.add(n);
-
-        int numNodes = candidates.size();
-        if (numNodes == 0) return;
-        Node[] nodes = candidates.toArray(new Node[numNodes]);
-        int rn = random.nextInt(numNodes);
-        antientropy(nodes[rn]);
-    }
-
-    /**
-     * Write a value synchronized from another node, bypassing some of the
-     * usual logic when a client writes data.  If the store is not known,
-     * this will automatically register it
-     * @param storeName the store name
-     * @param scope the scope for the store
-     * @param persist TODO
-     * @param key the key to write
-     * @param values a list of versions for the key to write
-     * @throws PersistException
-     */
-    public void writeSyncValue(String storeName, Scope scope,
-                               boolean persist,
-                               byte[] key, Iterable<Versioned<byte[]>> values)
-                                       throws PersistException {
-        SynchronizingStorageEngine store = storeRegistry.get(storeName);
-        if (store == null) {
-            store = storeRegistry.register(storeName, scope, persist);
-        }
-        store.writeSyncValue(new ByteArray(key), values);
-    }
-
-    /**
-     * Check whether any of the specified versions for the key are not older
-     * than the versions we already have
-     * @param storeName the store to check
-     * @param key the key to check
-     * @param versions an iterable over the versions
-     * @return true if we'd like a copy of the data indicated
-     * @throws SyncException
-     */
-    public boolean handleSyncOffer(String storeName,
-                                   byte[] key,
-                                   Iterable<VectorClock> versions)
-                                           throws SyncException {
-        SynchronizingStorageEngine store = storeRegistry.get(storeName);
-        if (store == null) return true;
-
-        List<Versioned<byte[]>> values = store.get(new ByteArray(key));
-        if (values == null || values.size() == 0) return true;
-
-        // check whether any of the versions are not older than what we have
-        for (VectorClock vc : versions) {
-            for (Versioned<byte[]> value : values) {
-                VectorClock existingVc = (VectorClock)value.getVersion();
-                if (!vc.compare(existingVc).equals(Occurred.BEFORE))
-                    return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Get access to the raw storage engine.  This is useful for some
-     * on-the-wire communication
-     * @param storeName the store name to get
-     * @return the {@link IStorageEngine}
-     * @throws UnknownStoreException
-     */
-    public IStorageEngine<ByteArray, byte[]> getRawStore(String storeName)
-            throws UnknownStoreException {
-        return getStoreInternal(storeName);
-    }
-
-    /**
-     * Return the threadpool
-     * @return the {@link IThreadPoolService}
-     */
-    public IThreadPoolService getThreadPool() {
-        return threadPool;
-    }
-
-    /**
-     * Queue a synchronization of the specified {@link KeyedValues} to all nodes
-     * assocatiated with the storage engine specified
-     * @param e the storage engine for the values
-     * @param kv the values to synchronize
-     */
-    public void queueSyncTask(SynchronizingStorageEngine e,
-                              ByteArray key, Versioned<byte[]> value) {
-        storeRegistry.queueHint(e.getName(), key, value);
-    }
-
-    @Override
-    public void addListener(String storeName, MappingStoreListener listener)
-            throws UnknownStoreException {
-        SynchronizingStorageEngine store = getStoreInternal(storeName);
-        store.addListener(listener);
-    }
-
-    /**
-     * Update the node configuration to add or remove nodes
-     * @throws FloodlightModuleException
-     */
-    public void updateConfiguration() {
-        if (updateConfigTask != null)
-            updateConfigTask.reschedule(500, TimeUnit.MILLISECONDS);
-    }
-
-    /**
-     * Retrieve the cursor, if any, for the given cursor ID
-     * @param cursorId the cursor ID
-     * @return the {@link Cursor}
-     */
-    public Cursor getCursor(int cursorId) {
-        return cursorMap.get(Integer.valueOf(cursorId));
-    }
-
-    /**
-     * Allocate a new cursor for the given store name
-     * @param storeName the store name
-     * @return the {@link Cursor}
-     * @throws SyncException
-     */
-    public Cursor newCursor(String storeName) throws UnknownStoreException {
-        IStore<ByteArray, byte[]> store = getStore(storeName);
-        int cursorId = rpcService.getTransactionId();
-        Cursor cursor = new Cursor(cursorId, store.entries());
-        cursorMap.put(Integer.valueOf(cursorId), cursor);
-        return cursor;
-    }
-
-    /**
-     * Close the given cursor and remove it from the map
-     * @param cursor the cursor to close
-     */
-    public void closeCursor(Cursor cursor) {
-        cursor.close();
-        cursorMap.remove(Integer.valueOf(cursor.getCursorId()));
-    }
-
-    // *******************
-    // AbstractSyncManager
-    // *******************
-
-    @Override
-    public IStore<ByteArray,byte[]> getStore(String storeName)
-            throws UnknownStoreException {
-        return getRawStore(storeName);
-    }
-
-    @Override
-    public short getLocalNodeId() {
-        Node l = clusterConfig.getNode();
-        if (l == null) return Short.MAX_VALUE;
-        return l.getNodeId();
-    }
-
-    @Override
-    public void shutdown() {
-        logger.debug("Shutting down Sync Manager: {} {}",
-                     clusterConfig.getNode().getHostname(),
-                     clusterConfig.getNode().getPort());
-
-        if (rpcService != null) {
-            rpcService.shutdown();
-        }
-        if (hintThreadPool != null) {
-            hintThreadPool.shutdown();
-        }
-        if (storeRegistry != null) {
-            storeRegistry.shutdown();
-        }
-        hintThreadPool = null;
-        rpcService = null;
-    }
-
-    // *****************
-    // IFloodlightModule
-    // *****************
-
-    @Override
-    public void init(FloodlightModuleContext context)
-            throws FloodlightModuleException {
-        threadPool = context.getServiceImpl(IThreadPoolService.class);
-        debugCounter = context.getServiceImpl(IDebugCounterService.class);
-        Map<String, String> config = context.getConfigParams(this);
-        storeRegistry = new StoreRegistry(this, config.get("dbPath"));
-
-        String[] configProviders =
-             {PropertyCCProvider.class.getName(),
-              SyncStoreCCProvider.class.getName(),
-              StorageCCProvider.class.getName(),
-              FallbackCCProvider.class.getName()};
-        try {
-            if (config.containsKey("persistenceEnabled")) {
-                persistenceEnabled =
-                        Boolean.parseBoolean(config.get("persistenceEnabled"));
-            }
-            if (config.containsKey("configProviders")) {
-                configProviders = config.get("configProviders").split(",");
-            }
-            DelegatingCCProvider dprovider = new DelegatingCCProvider();
-            for (String configProvider : configProviders) {
-                Class<?> cClass = Class.forName(configProvider);
-                IClusterConfigProvider provider =
-                        (IClusterConfigProvider) cClass.newInstance();
-                dprovider.addProvider(provider);
-            }
-            dprovider.init(this, context);
-            clusterConfigProvider = dprovider;
-        } catch (Exception e) {
-            throw new FloodlightModuleException("Could not instantiate config" +
-                    "providers " + Arrays.toString(configProviders), e);
-        }
-
-        String manualStoreString = config.get("manualStores");
-        if (manualStoreString != null) {
-            List<String> manualStores = null;
-            try {
-                manualStores =
-                        (new ObjectMapper()).readValue(manualStoreString,
-                                         new TypeReference<List<String>>() {});
-            } catch (Exception e) {
-                throw new FloodlightModuleException("Failed to parse sync " +
-                        "manager manual stores: " + manualStoreString, e);
-            }
-            for (String s : manualStores) {
-                registerStore(s, Scope.GLOBAL);
-            }
-        }
-        registerDebugCounters(context);
-    }
-
-    private void registerDebugCounters(FloodlightModuleContext context)
-            throws FloodlightModuleException {
-    	if (context != null) {
-    		debugCounter.registerModule(PACKAGE);
-    		counterHints = debugCounter.registerCounter(PACKAGE, "hints",
-    				"Queued sync events processed");
-    		counterSentValues = debugCounter.registerCounter(PACKAGE, "sent-values",
-    				"Values synced to remote node");
-    		counterReceivedValues = debugCounter.registerCounter(PACKAGE, "received-values",
-    				"Values received from remote node");
-    		counterPuts = debugCounter.registerCounter(PACKAGE, "puts",
-    				"Local puts to store");
-    		counterGets = debugCounter.registerCounter(PACKAGE, "gets",
-    				"Local gets from store");
-    		counterIterators = debugCounter.registerCounter(PACKAGE, "iterators",
-    				"Local iterators created over store");
-    		counterErrorRemote = debugCounter.registerCounter(PACKAGE, "error-remote",
-    				"Number of errors sent from remote clients",
-    				IDebugCounterService.MetaData.ERROR);
-    		counterErrorProcessing = debugCounter.registerCounter(PACKAGE,
-    				"error-processing",
-    				"Number of errors processing messages from remote clients",
-    				IDebugCounterService.MetaData.ERROR);
-    	}
-
-    }
-
-    @Override
-    public void startUp(FloodlightModuleContext context)
-            throws FloodlightModuleException {
-
-        rpcService = new RPCService(this, debugCounter);
-
-        cleanupTask = new SingletonTask(threadPool.getScheduledExecutor(),
-                                        new CleanupTask());
-        cleanupTask.reschedule(CLEANUP_INTERVAL +
-                               random.nextInt(30), TimeUnit.SECONDS);
-
-        antientropyTask = new SingletonTask(threadPool.getScheduledExecutor(),
-                                       new AntientropyTask());
-        antientropyTask.reschedule(ANTIENTROPY_INTERVAL +
-                                   random.nextInt(30), TimeUnit.SECONDS);
-
-        final ThreadGroup tg = new ThreadGroup("Hint Workers");
-        tg.setMaxPriority(Thread.NORM_PRIORITY - 2);
-        ThreadFactory f = new ThreadFactory() {
-            AtomicInteger id = new AtomicInteger();
-
-            @Override
-            public Thread newThread(Runnable runnable) {
-                return new Thread(tg, runnable,
-                                  "HintWorker-" + id.getAndIncrement());
-            }
-        };
-        hintThreadPool = Executors.newCachedThreadPool(f);
-        for (int i = 0; i < SYNC_WORKER_POOL; i++) {
-            hintThreadPool.execute(new HintWorker());
-        }
-
-        doUpdateConfiguration();
-        rpcService.run();
-
-        updateConfigTask =
-                new SingletonTask(threadPool.getScheduledExecutor(),
-                                  new UpdateConfigTask());
-        updateConfigTask.reschedule(CONFIG_RESCAN_INTERVAL, TimeUnit.SECONDS);
-    }
-
-    @Override
-    public Collection<Class<? extends IFloodlightService>>
-            getModuleDependencies() {
-        Collection<Class<? extends IFloodlightService>> l =
-                new ArrayList<Class<? extends IFloodlightService>>();
-        l.add(IThreadPoolService.class);
-        l.add(IStorageSourceService.class);
-        l.add(IDebugCounterService.class);
-        return l;
-    }
-
-    // ***************
-    // Local methods
-    // ***************
-
-    protected void doUpdateConfiguration()
-            throws FloodlightModuleException {
-
-        try {
-            ClusterConfig oldConfig = clusterConfig;
-            clusterConfig = clusterConfigProvider.getConfig();
-            if (clusterConfig.equals(oldConfig)) return;
-
-            logger.info("[{}] Updating sync configuration {}",
-                        clusterConfig.getNode().getNodeId(),
-                        clusterConfig);
-            if (oldConfig.getNode() != null &&
-                !clusterConfig.getNode().equals(oldConfig.getNode())) {
-                logger.info("[{}] Local node configuration changed; restarting sync" +
-                        "service", oldConfig.getNode().getNodeId());
-                shutdown();
-                startUp(null);
-            }
-
-            for (Node n : clusterConfig.getNodes()) {
-                Node existing = oldConfig.getNode(n.getNodeId());
-                if (existing != null && !n.equals(existing)) {
-                    // we already had this node's configuration, but it's
-                    // changed.  Disconnect from the node and let it
-                    // reinitialize
-                    logger.debug("[{}->{}] Configuration for node has changed",
-                                 getLocalNodeId(), n.getNodeId());
-                    rpcService.disconnectNode(n.getNodeId());
-                }
-            }
-            for (Node n : oldConfig.getNodes()) {
-                Node nn = clusterConfig.getNode(n.getNodeId());
-                if (nn == null) {
-                    // n is a node that doesn't appear in the new config
-                    logger.debug("[{}->{}] Disconnecting deconfigured node",
-                                 getLocalNodeId(), n.getNodeId());
-                    rpcService.disconnectNode(n.getNodeId());
-                }
-            }
-        } catch (Exception e) {
-            throw new FloodlightModuleException("Could not update " +
-                                                "configuration", e);
-        }
-    }
-
-    protected SynchronizingStorageEngine getStoreInternal(String storeName)
-            throws UnknownStoreException {
-        SynchronizingStorageEngine store = storeRegistry.get(storeName);
-        if (store == null) {
-            throw new UnknownStoreException("Store " + storeName +
-                                            " has not been registered");
-        }
-        return store;
-    }
-
-    private void sendSyncOffer(short nodeId, SyncMessage bsm)
-            throws InterruptedException {
-        SyncOfferMessage som = bsm.getSyncOffer();
-        if (!som.isSetVersions()) return;
-        if (logger.isTraceEnabled()) {
-            logger.trace("[{}->{}] Sending SyncOffer with {} elements",
-                         new Object[]{getLocalNodeId(), nodeId,
-                                      som.getVersionsSize()});
-        }
-
-        som.getHeader().setTransactionId(rpcService.getTransactionId());
-        rpcService.writeToNode(nodeId, bsm);
-    }
-
-    /**
-     * Periodically perform cleanup
-     * @author readams
-     */
-    protected class CleanupTask implements Runnable {
-        @Override
-        public void run() {
-            try {
-                if (rpcService != null)
-                    cleanup();
-            } catch (Exception e) {
-                logger.error("Cleanup task failed", e);
-            }
-
-            if (rpcService != null) {
-                cleanupTask.reschedule(CLEANUP_INTERVAL +
-                                       random.nextInt(30), TimeUnit.SECONDS);
-            }
-        }
-    }
-
-    /**
-     * Periodically perform antientropy
-     * @author readams
-     */
-    protected class AntientropyTask implements Runnable {
-        @Override
-        public void run() {
-            try {
-                if (rpcService != null)
-                    antientropy();
-            } catch (Exception e) {
-                logger.error("Antientropy task failed", e);
-            }
-
-            if (rpcService != null) {
-                antientropyTask.reschedule(ANTIENTROPY_INTERVAL +
-                                           random.nextInt(30),
-                                           TimeUnit.SECONDS);
-            }
-        }
-    }
-
-    /**
-     * Worker task to periodically rescan the configuration
-     * @author readams
-     */
-    protected class UpdateConfigTask implements Runnable {
-        @Override
-        public void run() {
-            try {
-                if (rpcService != null)
-                    doUpdateConfiguration();
-            } catch (Exception e) {
-                logger.error("Failed to update configuration", e);
-            }
-            if (rpcService != null) {
-                updateConfigTask.reschedule(CONFIG_RESCAN_INTERVAL,
-                                            TimeUnit.SECONDS);
-            }
-        }
-    }
-
-    /**
-     * Worker thread that will drain the sync item queue and write the
-     * appropriate messages to the node I/O channels
-     * @author readams
-     */
-    protected class HintWorker implements Runnable {
-        ArrayList<Hint> tasks = new ArrayList<Hint>(50);
-        protected Map<String, SyncMessage> messages =
-                new LinkedHashMap<String, SyncMessage>();
-
-        @Override
-        public void run() {
-            while (rpcService != null) {
-                try {
-                    // Batch up sync tasks so we use fewer, larger messages
-                    // XXX - todo - handle hints targeted to specific nodes
-                    storeRegistry.takeHints(tasks, 50);
-                    for (Hint task : tasks) {
-                        counterHints.increment();
-                        SynchronizingStorageEngine store =
-                                storeRegistry.get(task.getHintKey().
-                                                  getStoreName());
-                        SyncMessage bsm = getMessage(store);
-                        KeyedValues kv =
-                                TProtocolUtil.
-                                getTKeyedValues(task.getHintKey().getKey(),
-                                                task.getValues());
-                        bsm.getSyncValue().addToValues(kv);
-                    }
-
-                    Iterable<Node> nodes = getClusterConfig().getNodes();
-                    short localDomainId =
-                            getClusterConfig().getNode().getDomainId();
-                    short localNodeId =
-                            getClusterConfig().getNode().getNodeId();
-                    for (Node n : nodes) {
-                        if (localNodeId == n.getNodeId())
-                            continue;
-                        for (SyncMessage bsm : messages.values()) {
-                            SyncValueMessage svm = bsm.getSyncValue();
-                            if (svm.getStore().getScope().
-                                    equals(org.sdnplatform.sync.thrift.
-                                           Scope.LOCAL) &&
-                                           n.getDomainId() != localDomainId) {
-                                // This message is only for local domain
-                                continue;
-                            }
-
-                            svm.getHeader().
-                            setTransactionId(rpcService.getTransactionId());
-                            counterSentValues.add(bsm.getSyncValue().getValuesSize());
-                            rpcService.writeToNode(n.getNodeId(), bsm);
-                        }
-                    }
-                    tasks.clear();
-                    clearMessages();
-
-                } catch (Exception e) {
-                    logger.error("Error occured in synchronization worker", e);
-                }
-            }
-        }
-
-        /**
-         * Clear the current list of pending messages
-         */
-        private void clearMessages() {
-            messages.clear();
-        }
-
-        /**
-         * Allocate a partially-initialized {@link SyncMessage} object for
-         * the given store
-         * @param store the store
-         * @return the {@link SyncMessage} object
-         */
-        private SyncMessage getMessage(SynchronizingStorageEngine store) {
-            String storeName = store.getName();
-            SyncMessage bsm = messages.get(storeName);
-            if (bsm == null) {
-                bsm = TProtocolUtil.getTSyncValueMessage(storeName,
-                                                         store.getScope(),
-                                                         store.isPersistent());
-                messages.put(storeName, bsm);
-            }
-            return bsm;
-        }
-    }
+	protected static final Logger logger =
+			LoggerFactory.getLogger(SyncManager.class.getName());
+
+	protected IThreadPoolService threadPool;
+	protected IDebugCounterService debugCounter;
+
+	/**
+	 * The store registry holds the storage engines that provide
+	 * access to the data
+	 */
+	private StoreRegistry storeRegistry = null;
+	
+	private Timer timer;
+
+	private IClusterConfigProvider clusterConfigProvider;
+	private ClusterConfig clusterConfig = new ClusterConfig();
+
+	protected RPCService rpcService = null;
+
+	/**
+	 * Interval between cleanup tasks in seconds
+	 */
+	private static final int CLEANUP_INTERVAL = 60 * 60;
+
+	/**
+	 * Interval between antientropy tasks in seconds
+	 */
+	private static final int ANTIENTROPY_INTERVAL = 5 * 60;
+
+	/**
+	 * Interval between configuration rescans
+	 */
+	private static final int CONFIG_RESCAN_INTERVAL = 10;
+
+	/**
+	 * Task for performing periodic maintenance/cleanup on local stores
+	 */
+	private SingletonTask cleanupTask;
+
+	/**
+	 * Task for periodic antientropy between nodes
+	 */
+	private SingletonTask antientropyTask;
+
+	/**
+	 * Task to periodically rescan configuration
+	 */
+	private SingletonTask updateConfigTask;
+
+	/**
+	 * Number of {@link HintWorker} workers used to drain the queue of writes
+	 * that need to be sent to the connected nodes
+	 */
+	private static final int SYNC_WORKER_POOL = 2;
+
+	/**
+	 * A thread pool for the {@link HintWorker} threads.
+	 */
+	private ExecutorService hintThreadPool;
+
+	/**
+	 * Random number generator
+	 */
+	private final Random random = new Random();
+
+	/**
+	 * A map of the currently-allocated cursors
+	 */
+	private final Map<Integer, Cursor> cursorMap =
+			new ConcurrentHashMap<Integer, Cursor>();
+
+	/**
+	 * Whether to allow persistent stores or to use in-memory even
+	 * when persistence is requested
+	 */
+	private boolean persistenceEnabled = true;
+
+	private static final String PACKAGE =
+			ISyncService.class.getPackage().getName();
+
+	/**
+	 * Debug Counters
+	 */
+	public static IDebugCounter counterHints;
+	public static IDebugCounter counterSentValues;
+	public static IDebugCounter counterReceivedValues;
+	public static IDebugCounter counterPuts;
+	public static IDebugCounter counterGets;
+	public static IDebugCounter counterIterators;
+	public static IDebugCounter counterErrorRemote;
+	public static IDebugCounter counterErrorProcessing;
+
+	// ************
+	// ISyncService
+	// ************
+
+	@Override
+	public void registerStore(String storeName, Scope scope) {
+		try {
+			storeRegistry.register(storeName, scope, false);
+		} catch (PersistException e) {
+			// not possible
+			throw new SyncRuntimeException(e);
+		}
+	}
+
+	@Override
+	public void registerPersistentStore(String storeName, Scope scope)
+			throws PersistException {
+		storeRegistry.register(storeName, scope, persistenceEnabled);
+	}
+
+	// **************************
+	// SyncManager public methods
+	// **************************
+
+	/**
+	 * Get the cluster configuration object
+	 * @return the {@link ClusterConfig} object
+	 * @see ClusterConfig
+	 */
+	public ClusterConfig getClusterConfig() {
+		return clusterConfig;
+	}
+
+	/**
+	 * Perform periodic scheduled cleanup.  Note that this will be called
+	 * automatically and you shouldn't generally call it directly except for
+	 * testing
+	 * @throws SyncException
+	 */
+	public void cleanup() throws SyncException {
+		for (SynchronizingStorageEngine store : storeRegistry.values()) {
+			store.cleanupTask();
+		}
+	}
+
+	/**
+	 * Perform a synchronization with the node specified
+	 */
+	public void antientropy(Node node) {
+		if (!rpcService.isConnected(node.getNodeId())) return;
+
+		logger.info("[{}->{}] Synchronizing local state to remote node",
+				getLocalNodeId(), node.getNodeId());
+
+		for (SynchronizingStorageEngine store : storeRegistry.values()) {
+			if (Scope.LOCAL.equals(store.getScope())) {
+				if (node.getDomainId() !=
+						getClusterConfig().getNode().getDomainId())
+					continue;
+			} else if (Scope.UNSYNCHRONIZED.equals(store.getScope())) {
+				continue;
+			}
+
+			IClosableIterator<Entry<ByteArray,
+			List<Versioned<byte[]>>>> entries =
+			store.entries();
+			try {
+				SyncMessage bsm =
+						TProtocolUtil.getTSyncOfferMessage(store.getName(),
+								store.getScope(),
+								store.isPersistent());
+				int count = 0;
+				while (entries.hasNext()) {
+					if (!rpcService.isConnected(node.getNodeId())) return;
+
+					Entry<ByteArray, List<Versioned<byte[]>>> pair =
+							entries.next();
+					KeyedVersions kv =
+							TProtocolUtil.getTKeyedVersions(pair.getKey(),
+									pair.getValue());
+					bsm.getSyncOffer().addToVersions(kv);
+					count += 1;
+					if (count >= 50) {
+						sendSyncOffer(node.getNodeId(), bsm);
+						// realloc sync message - it is still queued up by netty!
+						bsm = TProtocolUtil.getTSyncOfferMessage(store.getName(),
+								store.getScope(),
+								store.isPersistent());
+						count = 0;
+					}
+				}
+				sendSyncOffer(node.getNodeId(), bsm);
+			} catch (InterruptedException e) {
+				// This can't really happen
+				throw new RuntimeException(e);
+			} finally {
+				entries.close();
+			}
+		}
+	}
+
+	/**
+	 * Communicate with a random node and do a full synchronization of the
+	 * all the stores on each node that have the appropriate scope.
+	 */
+	public void antientropy() {
+		ArrayList<Node> candidates = new ArrayList<Node>();
+		for (Node n : clusterConfig.getNodes())
+			if (rpcService.isConnected(n.getNodeId()))
+				candidates.add(n);
+
+		int numNodes = candidates.size();
+		if (numNodes == 0) return;
+		Node[] nodes = candidates.toArray(new Node[numNodes]);
+		int rn = random.nextInt(numNodes);
+		antientropy(nodes[rn]);
+	}
+
+	/**
+	 * Write a value synchronized from another node, bypassing some of the
+	 * usual logic when a client writes data.  If the store is not known,
+	 * this will automatically register it
+	 * @param storeName the store name
+	 * @param scope the scope for the store
+	 * @param persist TODO
+	 * @param key the key to write
+	 * @param values a list of versions for the key to write
+	 * @throws PersistException
+	 */
+	public void writeSyncValue(String storeName, Scope scope,
+			boolean persist,
+			byte[] key, Iterable<Versioned<byte[]>> values)
+					throws PersistException {
+		SynchronizingStorageEngine store = storeRegistry.get(storeName);
+		if (store == null) {
+			store = storeRegistry.register(storeName, scope, persist);
+		}
+		store.writeSyncValue(new ByteArray(key), values);
+	}
+
+	/**
+	 * Check whether any of the specified versions for the key are not older
+	 * than the versions we already have
+	 * @param storeName the store to check
+	 * @param key the key to check
+	 * @param versions an iterable over the versions
+	 * @return true if we'd like a copy of the data indicated
+	 * @throws SyncException
+	 */
+	public boolean handleSyncOffer(String storeName,
+			byte[] key,
+			Iterable<VectorClock> versions)
+					throws SyncException {
+		SynchronizingStorageEngine store = storeRegistry.get(storeName);
+		if (store == null) return true;
+
+		List<Versioned<byte[]>> values = store.get(new ByteArray(key));
+		if (values == null || values.size() == 0) return true;
+
+		// check whether any of the versions are not older than what we have
+		for (VectorClock vc : versions) {
+			for (Versioned<byte[]> value : values) {
+				VectorClock existingVc = (VectorClock)value.getVersion();
+				if (!vc.compare(existingVc).equals(Occurred.BEFORE))
+					return true;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Get access to the raw storage engine.  This is useful for some
+	 * on-the-wire communication
+	 * @param storeName the store name to get
+	 * @return the {@link IStorageEngine}
+	 * @throws UnknownStoreException
+	 */
+	public IStorageEngine<ByteArray, byte[]> getRawStore(String storeName)
+			throws UnknownStoreException {
+		return getStoreInternal(storeName);
+	}
+
+	/**
+	 * Return the threadpool
+	 * @return the {@link IThreadPoolService}
+	 */
+	public IThreadPoolService getThreadPool() {
+		return threadPool;
+	}
+
+	/**
+	 * Queue a synchronization of the specified {@link KeyedValues} to all nodes
+	 * assocatiated with the storage engine specified
+	 * @param e the storage engine for the values
+	 * @param kv the values to synchronize
+	 */
+	public void queueSyncTask(SynchronizingStorageEngine e,
+			ByteArray key, Versioned<byte[]> value) {
+		storeRegistry.queueHint(e.getName(), key, value);
+	}
+
+	@Override
+	public void addListener(String storeName, MappingStoreListener listener)
+			throws UnknownStoreException {
+		SynchronizingStorageEngine store = getStoreInternal(storeName);
+		store.addListener(listener);
+	}
+
+	/**
+	 * Update the node configuration to add or remove nodes
+	 * @throws FloodlightModuleException
+	 */
+	public void updateConfiguration() {
+		if (updateConfigTask != null)
+			updateConfigTask.reschedule(500, TimeUnit.MILLISECONDS);
+	}
+
+	/**
+	 * Retrieve the cursor, if any, for the given cursor ID
+	 * @param cursorId the cursor ID
+	 * @return the {@link Cursor}
+	 */
+	public Cursor getCursor(int cursorId) {
+		return cursorMap.get(Integer.valueOf(cursorId));
+	}
+
+	/**
+	 * Allocate a new cursor for the given store name
+	 * @param storeName the store name
+	 * @return the {@link Cursor}
+	 * @throws SyncException
+	 */
+	public Cursor newCursor(String storeName) throws UnknownStoreException {
+		IStore<ByteArray, byte[]> store = getStore(storeName);
+		int cursorId = rpcService.getTransactionId();
+		Cursor cursor = new Cursor(cursorId, store.entries());
+		cursorMap.put(Integer.valueOf(cursorId), cursor);
+		return cursor;
+	}
+
+	/**
+	 * Close the given cursor and remove it from the map
+	 * @param cursor the cursor to close
+	 */
+	public void closeCursor(Cursor cursor) {
+		cursor.close();
+		cursorMap.remove(Integer.valueOf(cursor.getCursorId()));
+	}
+
+	// *******************
+	// AbstractSyncManager
+	// *******************
+
+	@Override
+	public IStore<ByteArray,byte[]> getStore(String storeName)
+			throws UnknownStoreException {
+		return getRawStore(storeName);
+	}
+
+	@Override
+	public short getLocalNodeId() {
+		Node l = clusterConfig.getNode();
+		if (l == null) return Short.MAX_VALUE;
+		return l.getNodeId();
+	}
+
+	@Override
+	public void shutdown() {
+		logger.info("Shutting down Sync Manager: {} {}",
+				clusterConfig.getNode().getHostname(),
+				clusterConfig.getNode().getPort());
+
+		if (rpcService != null) {
+			rpcService.shutdown();
+		}
+		if (hintThreadPool != null) {
+			hintThreadPool.shutdown();
+		}
+		if (storeRegistry != null) {
+			storeRegistry.shutdown();
+		}
+		if (timer != null)
+            timer.stop();
+        timer = null;
+		hintThreadPool = null;
+		rpcService = null;
+	}
+
+	// *****************
+	// IFloodlightModule
+	// *****************
+
+	@Override
+	public void init(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+		threadPool = context.getServiceImpl(IThreadPoolService.class);
+		debugCounter = context.getServiceImpl(IDebugCounterService.class);
+		Map<String, String> config = context.getConfigParams(this);
+		storeRegistry = new StoreRegistry(this, config.get("dbPath"));
+
+		String[] configProviders =
+			{PropertyCCProvider.class.getName(),
+				SyncStoreCCProvider.class.getName(),
+				StorageCCProvider.class.getName(),
+				FallbackCCProvider.class.getName()};
+		try {
+			if (config.containsKey("persistenceEnabled")) {
+				persistenceEnabled =
+						Boolean.parseBoolean(config.get("persistenceEnabled"));
+			}
+			if (config.containsKey("configProviders")) {
+				configProviders = config.get("configProviders").split(",");
+			}
+			DelegatingCCProvider dprovider = new DelegatingCCProvider();
+			for (String configProvider : configProviders) {
+				Class<?> cClass = Class.forName(configProvider);
+				IClusterConfigProvider provider =
+						(IClusterConfigProvider) cClass.newInstance();
+				dprovider.addProvider(provider);
+			}
+			dprovider.init(this, context);
+			clusterConfigProvider = dprovider;
+		} catch (Exception e) {
+			throw new FloodlightModuleException("Could not instantiate config" +
+					"providers " + Arrays.toString(configProviders), e);
+		}
+
+		String manualStoreString = config.get("manualStores");
+		if (manualStoreString != null) {
+			List<String> manualStores = null;
+			try {
+				manualStores =
+						(new ObjectMapper()).readValue(manualStoreString,
+								new TypeReference<List<String>>() {});
+			} catch (Exception e) {
+				throw new FloodlightModuleException("Failed to parse sync " +
+						"manager manual stores: " + manualStoreString, e);
+			}
+			for (String s : manualStores) {
+				registerStore(s, Scope.GLOBAL);
+			}
+		}
+		registerDebugCounters(context);
+	}
+
+	private void registerDebugCounters(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+		if (context != null) {
+			debugCounter.registerModule(PACKAGE);
+			counterHints = debugCounter.registerCounter(PACKAGE, "hints",
+					"Queued sync events processed");
+			counterSentValues = debugCounter.registerCounter(PACKAGE, "sent-values",
+					"Values synced to remote node");
+			counterReceivedValues = debugCounter.registerCounter(PACKAGE, "received-values",
+					"Values received from remote node");
+			counterPuts = debugCounter.registerCounter(PACKAGE, "puts",
+					"Local puts to store");
+			counterGets = debugCounter.registerCounter(PACKAGE, "gets",
+					"Local gets from store");
+			counterIterators = debugCounter.registerCounter(PACKAGE, "iterators",
+					"Local iterators created over store");
+			counterErrorRemote = debugCounter.registerCounter(PACKAGE, "error-remote",
+					"Number of errors sent from remote clients",
+					IDebugCounterService.MetaData.ERROR);
+			counterErrorProcessing = debugCounter.registerCounter(PACKAGE,
+					"error-processing",
+					"Number of errors processing messages from remote clients",
+					IDebugCounterService.MetaData.ERROR);
+		}
+
+	}
+
+	@Override
+	public void startUp(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+
+		timer = new HashedWheelTimer();
+		rpcService = new RPCService(this, debugCounter, timer);
+
+		cleanupTask = new SingletonTask(threadPool.getScheduledExecutor(),
+				new CleanupTask());
+		cleanupTask.reschedule(CLEANUP_INTERVAL +
+				random.nextInt(30), TimeUnit.SECONDS);
+
+		antientropyTask = new SingletonTask(threadPool.getScheduledExecutor(),
+				new AntientropyTask());
+		antientropyTask.reschedule(ANTIENTROPY_INTERVAL +
+				random.nextInt(30), TimeUnit.SECONDS);
+
+		final ThreadGroup tg = new ThreadGroup("Hint Workers");
+		tg.setMaxPriority(Thread.NORM_PRIORITY - 2);
+		ThreadFactory f = new ThreadFactory() {
+			AtomicInteger id = new AtomicInteger();
+
+			@Override
+			public Thread newThread(Runnable runnable) {
+				return new Thread(tg, runnable,
+						"HintWorker-" + id.getAndIncrement());
+			}
+		};
+		hintThreadPool = Executors.newCachedThreadPool(f);
+		for (int i = 0; i < SYNC_WORKER_POOL; i++) {
+			hintThreadPool.execute(new HintWorker());
+		}
+
+		doUpdateConfiguration();
+		rpcService.run();
+
+		updateConfigTask =
+				new SingletonTask(threadPool.getScheduledExecutor(),
+						new UpdateConfigTask());
+		updateConfigTask.reschedule(CONFIG_RESCAN_INTERVAL, TimeUnit.SECONDS);
+	}
+
+	@Override
+	public Collection<Class<? extends IFloodlightService>>
+	getModuleDependencies() {
+		Collection<Class<? extends IFloodlightService>> l =
+				new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(IThreadPoolService.class);
+		l.add(IStorageSourceService.class);
+		l.add(IDebugCounterService.class);
+		return l;
+	}
+
+	// ***************
+	// Local methods
+	// ***************
+
+	protected void doUpdateConfiguration()
+			throws FloodlightModuleException {
+
+		try {
+			ClusterConfig oldConfig = clusterConfig;
+			clusterConfig = clusterConfigProvider.getConfig();
+			if (clusterConfig.equals(oldConfig)) return;
+
+			logger.info("[{}] Updating sync configuration {}",
+					clusterConfig.getNode().getNodeId(),
+					clusterConfig);
+			if (oldConfig.getNode() != null &&
+					!clusterConfig.getNode().equals(oldConfig.getNode())) {
+				logger.info("[{}] Local node configuration changed; restarting sync" +
+						"service", oldConfig.getNode().getNodeId());
+				shutdown();
+				startUp(null);
+			}
+
+			for (Node n : clusterConfig.getNodes()) {
+				Node existing = oldConfig.getNode(n.getNodeId());
+				if (existing != null && !n.equals(existing)) {
+					// we already had this node's configuration, but it's
+					// changed.  Disconnect from the node and let it
+					// reinitialize
+					logger.info("[{}->{}] Configuration for node has changed",
+							getLocalNodeId(), n.getNodeId());
+					rpcService.disconnectNode(n.getNodeId());
+				}
+			}
+			for (Node n : oldConfig.getNodes()) {
+				Node nn = clusterConfig.getNode(n.getNodeId());
+				if (nn == null) {
+					// n is a node that doesn't appear in the new config
+					logger.info("[{}->{}] Disconnecting deconfigured node",
+							getLocalNodeId(), n.getNodeId());
+					rpcService.disconnectNode(n.getNodeId());
+				}
+			}
+		} catch (Exception e) {
+			throw new FloodlightModuleException("Could not update " +
+					"configuration", e);
+		}
+	}
+
+	protected SynchronizingStorageEngine getStoreInternal(String storeName)
+			throws UnknownStoreException {
+		SynchronizingStorageEngine store = storeRegistry.get(storeName);
+		if (store == null) {
+			throw new UnknownStoreException("Store " + storeName +
+					" has not been registered");
+		}
+		return store;
+	}
+
+	private void sendSyncOffer(short nodeId, SyncMessage bsm)
+			throws InterruptedException {
+		SyncOfferMessage som = bsm.getSyncOffer();
+		if (!som.isSetVersions()) return;
+		if (logger.isTraceEnabled()) {
+			logger.trace("[{}->{}] Sending SyncOffer with {} elements",
+					new Object[]{getLocalNodeId(), nodeId,
+					som.getVersionsSize()});
+		}
+
+		som.getHeader().setTransactionId(rpcService.getTransactionId());
+		rpcService.writeToNode(nodeId, bsm);
+	}
+
+	/**
+	 * Periodically perform cleanup
+	 * @author readams
+	 */
+	protected class CleanupTask implements Runnable {
+		@Override
+		public void run() {
+			try {
+				if (rpcService != null)
+					cleanup();
+			} catch (Exception e) {
+				logger.error("Cleanup task failed", e);
+			}
+
+			if (rpcService != null) {
+				cleanupTask.reschedule(CLEANUP_INTERVAL +
+						random.nextInt(30), TimeUnit.SECONDS);
+			}
+		}
+	}
+
+	/**
+	 * Periodically perform antientropy
+	 * @author readams
+	 */
+	protected class AntientropyTask implements Runnable {
+		@Override
+		public void run() {
+			try {
+				if (rpcService != null)
+					antientropy();
+			} catch (Exception e) {
+				logger.error("Antientropy task failed", e);
+			}
+
+			if (rpcService != null) {
+				antientropyTask.reschedule(ANTIENTROPY_INTERVAL +
+						random.nextInt(30),
+						TimeUnit.SECONDS);
+			}
+		}
+	}
+
+	/**
+	 * Worker task to periodically rescan the configuration
+	 * @author readams
+	 */
+	protected class UpdateConfigTask implements Runnable {
+		@Override
+		public void run() {
+			try {
+				if (rpcService != null)
+					doUpdateConfiguration();
+			} catch (Exception e) {
+				logger.error("Failed to update configuration", e);
+			}
+			if (rpcService != null) {
+				updateConfigTask.reschedule(CONFIG_RESCAN_INTERVAL,
+						TimeUnit.SECONDS);
+			}
+		}
+	}
+
+	/**
+	 * Worker thread that will drain the sync item queue and write the
+	 * appropriate messages to the node I/O channels
+	 * @author readams
+	 */
+	protected class HintWorker implements Runnable {
+		ArrayList<Hint> tasks = new ArrayList<Hint>(50);
+		protected Map<String, SyncMessage> messages =
+				new LinkedHashMap<String, SyncMessage>();
+
+		@Override
+		public void run() {
+			while (rpcService != null) {
+				try {
+					// Batch up sync tasks so we use fewer, larger messages
+					// XXX - todo - handle hints targeted to specific nodes
+					storeRegistry.takeHints(tasks, 50);
+					for (Hint task : tasks) {
+						counterHints.increment();
+						SynchronizingStorageEngine store =
+								storeRegistry.get(task.getHintKey().
+										getStoreName());
+						SyncMessage bsm = getMessage(store);
+						KeyedValues kv =
+								TProtocolUtil.
+								getTKeyedValues(task.getHintKey().getKey(),
+										task.getValues());
+						bsm.getSyncValue().addToValues(kv);
+					}
+
+					Iterable<Node> nodes = getClusterConfig().getNodes();
+					short localDomainId =
+							getClusterConfig().getNode().getDomainId();
+					short localNodeId =
+							getClusterConfig().getNode().getNodeId();
+					for (Node n : nodes) {
+						if (localNodeId == n.getNodeId())
+							continue;
+						for (SyncMessage bsm : messages.values()) {
+							SyncValueMessage svm = bsm.getSyncValue();
+							if (svm.getStore().getScope().
+									equals(org.sdnplatform.sync.thrift.
+											Scope.LOCAL) &&
+											n.getDomainId() != localDomainId) {
+								// This message is only for local domain
+								continue;
+							}
+
+							svm.getHeader().setTransactionId(rpcService.getTransactionId());
+							counterSentValues.add(bsm.getSyncValue().getValuesSize());
+							rpcService.writeToNode(n.getNodeId(), bsm);
+						}
+					}
+					tasks.clear();
+					clearMessages();
+
+				} catch (Exception e) {
+					logger.error("Error occured in synchronization worker", e);
+				}
+			}
+		}
+
+		/**
+		 * Clear the current list of pending messages
+		 */
+		private void clearMessages() {
+			messages.clear();
+		}
+
+		/**
+		 * Allocate a partially-initialized {@link SyncMessage} object for
+		 * the given store
+		 * @param store the store
+		 * @return the {@link SyncMessage} object
+		 */
+		private SyncMessage getMessage(SynchronizingStorageEngine store) {
+			String storeName = store.getName();
+			SyncMessage bsm = messages.get(storeName);
+			if (bsm == null) {
+				bsm = TProtocolUtil.getTSyncValueMessage(storeName,
+						store.getScope(),
+						store.isPersistent());
+				messages.put(storeName, bsm);
+			}
+			return bsm;
+		}
+	}
 }
diff --git a/src/main/java/org/sdnplatform/sync/internal/config/SyncStoreCCProvider.java b/src/main/java/org/sdnplatform/sync/internal/config/SyncStoreCCProvider.java
index f1efbe49f5ae79ce1bfb529eeb720cb681085e54..4cd7442dfd745431dea125eb2ec544ec97326382 100644
--- a/src/main/java/org/sdnplatform/sync/internal/config/SyncStoreCCProvider.java
+++ b/src/main/java/org/sdnplatform/sync/internal/config/SyncStoreCCProvider.java
@@ -26,7 +26,7 @@ import org.sdnplatform.sync.Versioned;
 import org.sdnplatform.sync.error.ObsoleteVersionException;
 import org.sdnplatform.sync.error.SyncException;
 import org.sdnplatform.sync.internal.SyncManager;
-import org.sdnplatform.sync.internal.config.bootstrap.Bootstrap;
+import org.sdnplatform.sync.internal.config.bootstrap.BootstrapClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -345,7 +345,7 @@ public class SyncStoreCCProvider
                         hosts.add(HostAndPort.fromString(s).
                                       withDefaultPort(6642));
                     }
-                    Bootstrap bs = new Bootstrap(syncManager,
+                    BootstrapClient bs = new BootstrapClient(syncManager,
                                                  authScheme,
                                                  keyStorePath, 
                                                  keyStorePassword);
diff --git a/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapChannelHandler.java b/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapChannelHandler.java
index 4e7802b7889db6c85b077fe9bc60a52ab1249973..1272dd849d0578d7a28a564bc6a05bd49206db13 100644
--- a/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapChannelHandler.java
+++ b/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapChannelHandler.java
@@ -1,8 +1,7 @@
 package org.sdnplatform.sync.internal.config.bootstrap;
 
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelStateEvent;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
 import org.sdnplatform.sync.IStoreClient;
 import org.sdnplatform.sync.Versioned;
 import org.sdnplatform.sync.error.AuthException;
@@ -30,10 +29,10 @@ public class BootstrapChannelHandler extends AbstractRPCChannelHandler {
     protected static final Logger logger =
             LoggerFactory.getLogger(BootstrapChannelHandler.class);
 
-    private Bootstrap bootstrap;
+    private BootstrapClient bootstrap;
     private Short remoteNodeId;
     
-    public BootstrapChannelHandler(Bootstrap bootstrap) {
+    public BootstrapChannelHandler(BootstrapClient bootstrap) {
         super();
         this.bootstrap = bootstrap;
     }
@@ -43,9 +42,9 @@ public class BootstrapChannelHandler extends AbstractRPCChannelHandler {
     // ****************************
 
     @Override
-    public void channelOpen(ChannelHandlerContext ctx, 
-                            ChannelStateEvent e) throws Exception {
-        bootstrap.cg.add(ctx.getChannel());
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        bootstrap.getChannelGroup().add(ctx.channel());
+        super.channelActive(ctx);
     }
 
     // ******************************************
@@ -73,7 +72,7 @@ public class BootstrapChannelHandler extends AbstractRPCChannelHandler {
         SyncMessage bsm = 
                 new SyncMessage(MessageType.CLUSTER_JOIN_REQUEST);
         bsm.setClusterJoinRequest(cjrm);
-        channel.write(bsm);
+        channel.writeAndFlush(bsm);
     }
 
     @Override
@@ -107,7 +106,7 @@ public class BootstrapChannelHandler extends AbstractRPCChannelHandler {
             bootstrap.succeeded = true;
         } catch (Exception e) {
             logger.error("Error processing cluster join response", e);
-            channel.write(getError(response.getHeader().getTransactionId(), e, 
+            channel.writeAndFlush(getError(response.getHeader().getTransactionId(), e, 
                                    MessageType.CLUSTER_JOIN_RESPONSE));
         }
         channel.disconnect();
diff --git a/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapChannelInitializer.java b/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapChannelInitializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..845836ae1db61d1a46a78bee1720474fa98c71a2
--- /dev/null
+++ b/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapChannelInitializer.java
@@ -0,0 +1,39 @@
+package org.sdnplatform.sync.internal.config.bootstrap;
+
+import org.sdnplatform.sync.internal.rpc.SyncMessageDecoder;
+import org.sdnplatform.sync.internal.rpc.SyncMessageEncoder;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.util.Timer;
+
+
+
+public class BootstrapChannelInitializer extends ChannelInitializer<Channel> {
+    private final BootstrapClient bootstrap;
+    private static final int maxFrameSize = 1024 * 1024 * 10;
+    protected Timer timer;
+
+    public BootstrapChannelInitializer(Timer timer, BootstrapClient bootstrap) {
+        super();
+        this.timer = timer;
+        this.bootstrap = bootstrap;
+    }
+
+    @Override
+    protected void initChannel(Channel ch) throws Exception {
+        BootstrapChannelHandler handler =
+                new BootstrapChannelHandler(bootstrap);
+
+        ChannelPipeline pipeline = ch.pipeline();
+
+        pipeline.addLast("syncMessageDecoder", new SyncMessageDecoder(maxFrameSize));
+
+        pipeline.addLast("syncMessageEncoder", new SyncMessageEncoder());
+
+        pipeline.addLast("timeout", new BootstrapTimeoutHandler(timer, 10));
+
+        pipeline.addLast("handler", handler);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/Bootstrap.java b/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapClient.java
similarity index 54%
rename from src/main/java/org/sdnplatform/sync/internal/config/bootstrap/Bootstrap.java
rename to src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapClient.java
index 6b994e3c8dc3960eb716d1f822bf8d9a45a9c578..8133635241d5ce9e954c345d565d7a5e300f9f14 100644
--- a/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/Bootstrap.java
+++ b/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapClient.java
@@ -2,16 +2,22 @@ package org.sdnplatform.sync.internal.config.bootstrap;
 
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import org.jboss.netty.bootstrap.ClientBootstrap;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelFuture;
-import org.jboss.netty.channel.group.ChannelGroup;
-import org.jboss.netty.channel.group.DefaultChannelGroup;
-import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.timeout.TimeoutException;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.GlobalEventExecutor;
+
 import org.sdnplatform.sync.error.SyncException;
 import org.sdnplatform.sync.internal.SyncManager;
 import org.sdnplatform.sync.internal.config.AuthScheme;
@@ -27,14 +33,14 @@ import com.google.common.net.HostAndPort;
  * local system store
  * @author readams
  */
-public class Bootstrap {
+public class BootstrapClient {
     protected static final Logger logger =
-            LoggerFactory.getLogger(Bootstrap.class);
+            LoggerFactory.getLogger(BootstrapClient.class);
     
     /**
      * Channel group that will hold all our channels
      */
-    protected ChannelGroup cg;
+    private ChannelGroup cg;
 
     /**
      * Transaction ID used in message headers in the RPC protocol
@@ -49,15 +55,16 @@ public class Bootstrap {
     protected final String keyStorePath;
     protected final String keyStorePassword;
     
-    ExecutorService bossExecutor = null;
-    ExecutorService workerExecutor = null;
-    ClientBootstrap bootstrap = null;
-    BootstrapPipelineFactory pipelineFactory;
+    EventLoopGroup workerExecutor = null;
+    Bootstrap bootstrap = null;
+    BootstrapChannelInitializer pipelineFactory;
     
     protected Node localNode;
     protected volatile boolean succeeded = false;
+    
+    private Timer timer;
 
-    public Bootstrap(SyncManager syncManager, AuthScheme authScheme,
+    public BootstrapClient(SyncManager syncManager, AuthScheme authScheme,
                      String keyStorePath, String keyStorePassword) {
         super();
         this.syncManager = syncManager;
@@ -67,25 +74,23 @@ public class Bootstrap {
     }
 
     public void init() throws SyncException {
-        cg = new DefaultChannelGroup("Cluster Bootstrap");
-
-        bossExecutor = Executors.newCachedThreadPool();
-        workerExecutor = Executors.newCachedThreadPool();
+        cg = new DefaultChannelGroup("Cluster Bootstrap", GlobalEventExecutor.INSTANCE);
 
-        bootstrap =
-                new ClientBootstrap(new NioClientSocketChannelFactory(bossExecutor,
-                                                                      workerExecutor));
-        bootstrap.setOption("child.reuseAddr", true);
-        bootstrap.setOption("child.keepAlive", true);
-        bootstrap.setOption("child.tcpNoDelay", true);
-        bootstrap.setOption("child.sendBufferSize", 
-                            RPCService.SEND_BUFFER_SIZE);
-        bootstrap.setOption("child.receiveBufferSize", 
-                            RPCService.SEND_BUFFER_SIZE);
-        bootstrap.setOption("child.connectTimeoutMillis", 
-                            RPCService.CONNECT_TIMEOUT);
-        pipelineFactory = new BootstrapPipelineFactory(this);
-        bootstrap.setPipelineFactory(pipelineFactory);
+        workerExecutor = new NioEventLoopGroup();
+        timer = new HashedWheelTimer();
+        
+        bootstrap = new Bootstrap()
+        .group(workerExecutor)
+        .channel(NioSocketChannel.class)
+        .option(ChannelOption.SO_REUSEADDR, true)
+        .option(ChannelOption.SO_KEEPALIVE, true)
+        .option(ChannelOption.TCP_NODELAY, true)
+        .option(ChannelOption.SO_SNDBUF, RPCService.SEND_BUFFER_SIZE)
+        .option(ChannelOption.SO_RCVBUF, RPCService.SEND_BUFFER_SIZE)
+        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, RPCService.CONNECT_TIMEOUT);
+        
+        pipelineFactory = new BootstrapChannelInitializer(timer, this);
+        bootstrap.handler(pipelineFactory);
     }
     
     public void shutdown() {
@@ -93,18 +98,20 @@ public class Bootstrap {
             cg.close().awaitUninterruptibly();
             cg = null;
         }
-        if (bootstrap != null)
-            bootstrap.releaseExternalResources();
         bootstrap = null;
-        if (pipelineFactory != null)
-            pipelineFactory.releaseExternalResources();
         pipelineFactory = null;
-        if (workerExecutor != null)
-            workerExecutor.shutdown();
-        workerExecutor = null;
-        if (bossExecutor != null)
-            bossExecutor.shutdown();
-        bossExecutor = null;
+        if (workerExecutor != null) {
+            try {
+				workerExecutor.shutdownGracefully();
+			} catch (TimeoutException e) {
+				logger.warn("Error waiting for gracefull shutdown of BootstrapClient {}", e);
+			}
+            workerExecutor = null;
+        }
+        if (timer != null) {
+        	timer.stop();
+        	timer = null;
+        }
     }
     
     public boolean bootstrap(HostAndPort seed, 
@@ -116,20 +123,24 @@ public class Bootstrap {
         ChannelFuture future = bootstrap.connect(sa);
         future.awaitUninterruptibly();
         if (!future.isSuccess()) {
-            logger.debug("Could not connect to " + seed, future.getCause());
+            logger.debug("Could not connect to " + seed, future.cause());
             return false;
         }
-        Channel channel = future.getChannel();
+        Channel channel = future.channel();
         logger.debug("[{}] Connected to {}", 
                      localNode != null ? localNode.getNodeId() : null,
                      seed);
         
         try {
-            channel.getCloseFuture().await();
+            channel.closeFuture().await();
         } catch (InterruptedException e) {
             logger.debug("Interrupted while waiting for bootstrap");
             return succeeded;
         }
         return succeeded;
     }
+    
+    public ChannelGroup getChannelGroup() {
+    	return cg;
+    }
 }
diff --git a/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapPipelineFactory.java b/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapPipelineFactory.java
deleted file mode 100644
index f8850150fed79064b32ae678e1144bac79c2cbf0..0000000000000000000000000000000000000000
--- a/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapPipelineFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.sdnplatform.sync.internal.config.bootstrap;
-
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.channel.ChannelPipelineFactory;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.util.ExternalResourceReleasable;
-import org.jboss.netty.util.HashedWheelTimer;
-import org.jboss.netty.util.Timer;
-import org.sdnplatform.sync.internal.rpc.ThriftFrameDecoder;
-import org.sdnplatform.sync.internal.rpc.ThriftFrameEncoder;
-
-public class BootstrapPipelineFactory 
-    implements ChannelPipelineFactory, ExternalResourceReleasable {
-    private Bootstrap bootstrap;
-    private static final int maxFrameSize = 1024 * 1024 * 10;
-    protected Timer timer;
-    
-    public BootstrapPipelineFactory(Bootstrap bootstrap) {
-        super();
-        this.bootstrap = bootstrap;
-        this.timer = new HashedWheelTimer();
-    }
-
-    @Override
-    public ChannelPipeline getPipeline() throws Exception {
-        BootstrapChannelHandler handler = 
-                new BootstrapChannelHandler(bootstrap);
-        ChannelPipeline pipeline = Channels.pipeline();
-
-        pipeline.addLast("frameDecoder",
-                         new ThriftFrameDecoder(maxFrameSize));
-        pipeline.addLast("frameEncoder",
-                         new ThriftFrameEncoder());
-        pipeline.addLast("timeout",
-                         new BootstrapTimeoutHandler(timer, 10));
-
-        pipeline.addLast("handler", handler);
-
-        return pipeline;
-    }
-
-    @Override
-    public void releaseExternalResources() {
-        timer.stop();
-    }    
-}
diff --git a/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapTimeoutHandler.java b/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapTimeoutHandler.java
index fa2de52701d9f7f75ac5389d87bc1d7fb55c9414..9ad9cb7d30a449e460646a030f7e8b8556fa24d4 100644
--- a/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapTimeoutHandler.java
+++ b/src/main/java/org/sdnplatform/sync/internal/config/bootstrap/BootstrapTimeoutHandler.java
@@ -18,18 +18,16 @@ package org.sdnplatform.sync.internal.config.bootstrap;
 
 import java.util.concurrent.TimeUnit;
 
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
-import org.jboss.netty.util.Timeout;
-import org.jboss.netty.util.Timer;
-import org.jboss.netty.util.TimerTask;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
 
 /**
  * Trigger a timeout if the bootstrap process stalls
  */
-public class BootstrapTimeoutHandler 
-    extends SimpleChannelUpstreamHandler {
+public class BootstrapTimeoutHandler extends ChannelInboundHandlerAdapter {
     
     final Timer timer;
     final long timeoutNanos;
@@ -44,22 +42,23 @@ public class BootstrapTimeoutHandler
     }
     
     @Override
-    public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
+    public void channelActive(ChannelHandlerContext ctx)
             throws Exception {
         if (timeoutNanos > 0) {
             timeout = timer.newTimeout(new HandshakeTimeoutTask(ctx), 
                                        timeoutNanos, TimeUnit.NANOSECONDS);
         }
-        ctx.sendUpstream(e);
+        super.channelActive(ctx);
     }
     
     @Override
-    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
+    public void channelInactive(ChannelHandlerContext ctx)
             throws Exception {
         if (timeout != null) {
             timeout.cancel();
             timeout = null;
         }
+        super.channelInactive(ctx);
     }
     
     private final class HandshakeTimeoutTask implements TimerTask {
@@ -76,10 +75,10 @@ public class BootstrapTimeoutHandler
                 return;
             }
 
-            if (!ctx.getChannel().isOpen()) {
+            if (!ctx.channel().isOpen()) {
                 return;
             }
-            ctx.getChannel().disconnect();
+            ctx.channel().disconnect();
         }
     }
 }
diff --git a/src/main/java/org/sdnplatform/sync/internal/remote/RSHandshakeTimeoutHandler.java b/src/main/java/org/sdnplatform/sync/internal/remote/RSHandshakeTimeoutHandler.java
index b8cf1b7e9aa33791d34e2f5ea55d8aacb85886ea..4c6ad767c2ba91ff7f622e98db20311fb4d320e8 100644
--- a/src/main/java/org/sdnplatform/sync/internal/remote/RSHandshakeTimeoutHandler.java
+++ b/src/main/java/org/sdnplatform/sync/internal/remote/RSHandshakeTimeoutHandler.java
@@ -18,18 +18,16 @@ package org.sdnplatform.sync.internal.remote;
 
 import java.util.concurrent.TimeUnit;
 
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
-import org.jboss.netty.util.Timeout;
-import org.jboss.netty.util.Timer;
-import org.jboss.netty.util.TimerTask;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
 
 /**
  * Trigger a timeout if the bootstrap process stalls
  */
-public class RSHandshakeTimeoutHandler 
-    extends SimpleChannelUpstreamHandler {
+public class RSHandshakeTimeoutHandler extends ChannelInboundHandlerAdapter {
     
     final Timer timer;
     final long timeoutNanos;
@@ -46,22 +44,23 @@ public class RSHandshakeTimeoutHandler
     }
     
     @Override
-    public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
+    public void channelActive(ChannelHandlerContext ctx)
             throws Exception {
         if (timeoutNanos > 0) {
             timeout = timer.newTimeout(new HandshakeTimeoutTask(ctx), 
                                        timeoutNanos, TimeUnit.NANOSECONDS);
         }
-        ctx.sendUpstream(e);
+        super.channelActive(ctx);
     }
     
     @Override
-    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
+    public void channelInactive(ChannelHandlerContext ctx)
             throws Exception {
         if (timeout != null) {
             timeout.cancel();
             timeout = null;
         }
+        super.channelInactive(ctx);
     }
     
     private final class HandshakeTimeoutTask implements TimerTask {
@@ -78,11 +77,11 @@ public class RSHandshakeTimeoutHandler
                 return;
             }
 
-            if (!ctx.getChannel().isOpen()) {
+            if (!ctx.channel().isOpen()) {
                 return;
             }
             if (channelHandler.syncManager.ready == false)
-                ctx.getChannel().disconnect();
+                ctx.channel().disconnect();
         }
     }
 }
diff --git a/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncChannelHandler.java b/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncChannelHandler.java
index 9ce4ecbb375e24949c093847b6d44c5f10942881..38ce819579c27f611e4049acd73d95d0b9f9ee9a 100644
--- a/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncChannelHandler.java
+++ b/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncChannelHandler.java
@@ -2,9 +2,8 @@ package org.sdnplatform.sync.internal.remote;
 
 import java.util.List;
 
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelStateEvent;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
 import org.sdnplatform.sync.Versioned;
 import org.sdnplatform.sync.error.AuthException;
 import org.sdnplatform.sync.error.SyncException;
@@ -42,17 +41,17 @@ public class RemoteSyncChannelHandler extends AbstractRPCChannelHandler {
     // ****************************
     
     @Override
-    public void channelOpen(ChannelHandlerContext ctx, 
-                            ChannelStateEvent e) throws Exception {
-        syncManager.cg.add(ctx.getChannel());
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        syncManager.cg.add(ctx.channel());
+        super.channelActive(ctx);
     }
 
     @Override
-    public void channelDisconnected(ChannelHandlerContext ctx,
-                                    ChannelStateEvent e) throws Exception {
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
         this.syncManager.channel = null;
         syncManager.ready = false;
         syncManager.channelDisconnected(null);
+        super.channelInactive(ctx);
     }
 
     // ******************************************
diff --git a/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncChannelInitializer.java b/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncChannelInitializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..bba84651d504a73698085bb1d9d9cea3e5745ef9
--- /dev/null
+++ b/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncChannelInitializer.java
@@ -0,0 +1,47 @@
+package org.sdnplatform.sync.internal.remote;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.util.Timer;
+
+import org.sdnplatform.sync.internal.rpc.SyncMessageDecoder;
+import org.sdnplatform.sync.internal.rpc.SyncMessageEncoder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Pipeline factory for the remote sync service
+ * @author readams
+ */
+public class RemoteSyncChannelInitializer extends ChannelInitializer<Channel> {
+    protected static final Logger logger =
+            LoggerFactory.getLogger(RemoteSyncChannelInitializer.class.getName());
+
+    private final RemoteSyncManager syncManager;
+    private final Timer timer;
+
+    private static final int maxFrameSize = 1024 * 1024 * 10;
+
+    public RemoteSyncChannelInitializer(Timer timer, RemoteSyncManager syncManager) {
+        super();
+        this.syncManager = syncManager;
+        this.timer = timer;
+    }
+
+    @Override
+    protected void initChannel(Channel ch) throws Exception {
+        RemoteSyncChannelHandler channelHandler =
+                new RemoteSyncChannelHandler(syncManager);
+
+        ChannelPipeline pipeline = ch.pipeline();
+
+        pipeline.addLast("syncMessageDecoder", new SyncMessageDecoder(maxFrameSize));
+
+        pipeline.addLast("syncMessageEncoder", new SyncMessageEncoder());
+
+        pipeline.addLast("timeout", new RSHandshakeTimeoutHandler(channelHandler, timer, 3));
+
+        pipeline.addLast("handler", channelHandler);
+    }
+}
\ No newline at end of file
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 efcc3ec67d602d722420d0b1989a77b97045a720..23b8e7bc24b93d37d565081c5da17252be97a95f 100644
--- a/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncManager.java
+++ b/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncManager.java
@@ -5,19 +5,23 @@ import java.net.SocketAddress;
 import java.util.Collection;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import org.jboss.netty.bootstrap.ClientBootstrap;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelFuture;
-import org.jboss.netty.channel.group.ChannelGroup;
-import org.jboss.netty.channel.group.DefaultChannelGroup;
-import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.concurrent.GlobalEventExecutor;
+
 import org.sdnplatform.sync.error.RemoteStoreException;
 import org.sdnplatform.sync.error.SyncException;
 import org.sdnplatform.sync.error.SyncRuntimeException;
@@ -53,10 +57,9 @@ public class RemoteSyncManager extends AbstractSyncManager {
     /**
      * Channel group that will hold all our channels
      */
-    final ChannelGroup cg = new DefaultChannelGroup("Internal RPC");
-    RemoteSyncPipelineFactory pipelineFactory;
-    ExecutorService bossExecutor;
-    ExecutorService workerExecutor;
+    final ChannelGroup cg = new DefaultChannelGroup("Internal RPC", GlobalEventExecutor.INSTANCE);
+    RemoteSyncChannelInitializer pipelineFactory;
+    EventLoopGroup workerExecutor;
 
     /**
      * Active connection to server
@@ -75,7 +78,7 @@ public class RemoteSyncManager extends AbstractSyncManager {
     /**
      * Client bootstrap
      */
-    protected ClientBootstrap clientBootstrap;
+    protected Bootstrap clientBootstrap;
     
     /**
      * Transaction ID used in message headers in the RPC protocol
@@ -92,6 +95,11 @@ public class RemoteSyncManager extends AbstractSyncManager {
      */
     protected int port = 6642;
     
+    /**
+     * Timer for Netty
+     */
+    private HashedWheelTimer timer;
+    
     protected AuthScheme authScheme;
     protected String keyStorePath;
     protected String keyStorePassword;
@@ -153,19 +161,16 @@ public class RemoteSyncManager extends AbstractSyncManager {
                 logger.debug("Failed to cleanly shut down remote sync");
                 return;
             }
-            if (clientBootstrap != null) {
-                clientBootstrap.releaseExternalResources();
-            }
             clientBootstrap = null;
-            if (pipelineFactory != null)
-                pipelineFactory.releaseExternalResources();
             pipelineFactory = null;
-            if (workerExecutor != null)
-                workerExecutor.shutdown();
-            workerExecutor = null;
-            if (bossExecutor != null)
-                bossExecutor.shutdown();
-            bossExecutor = null;
+            if (workerExecutor != null) {
+            	workerExecutor.shutdownGracefully();
+            	workerExecutor = null;
+            }
+            if (timer != null) {
+            	timer.stop();
+            	timer = null;
+            }
         } catch (InterruptedException e) {
             logger.debug("Interrupted while shutting down remote sync");
         }
@@ -195,24 +200,22 @@ public class RemoteSyncManager extends AbstractSyncManager {
     public void startUp(FloodlightModuleContext context) 
             throws FloodlightModuleException {
         shutdown = false;
-        bossExecutor = Executors.newCachedThreadPool();
-        workerExecutor = Executors.newCachedThreadPool();
+        workerExecutor = new NioEventLoopGroup();
+        timer = new HashedWheelTimer();
+        
+        pipelineFactory = new RemoteSyncChannelInitializer(timer, this);
+        
+        final Bootstrap bootstrap = new Bootstrap()
+        .channel(NioSocketChannel.class)
+        .group(workerExecutor)
+        .option(ChannelOption.SO_REUSEADDR, true)
+        .option(ChannelOption.SO_KEEPALIVE, true)
+        .option(ChannelOption.TCP_NODELAY, true)
+        .option(ChannelOption.SO_SNDBUF, RPCService.SEND_BUFFER_SIZE)
+        .option(ChannelOption.SO_RCVBUF, RPCService.SEND_BUFFER_SIZE)
+        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, RPCService.CONNECT_TIMEOUT)
+        .handler(pipelineFactory);
         
-        final ClientBootstrap bootstrap =
-                new ClientBootstrap(
-                     new NioClientSocketChannelFactory(bossExecutor,
-                                                       workerExecutor));
-        bootstrap.setOption("child.reuseAddr", true);
-        bootstrap.setOption("child.keepAlive", true);
-        bootstrap.setOption("child.tcpNoDelay", true);
-        bootstrap.setOption("child.sendBufferSize", 
-                            RPCService.SEND_BUFFER_SIZE);
-        bootstrap.setOption("child.receiveBufferSize", 
-                            RPCService.SEND_BUFFER_SIZE);
-        bootstrap.setOption("child.connectTimeoutMillis", 
-                            RPCService.CONNECT_TIMEOUT);
-        pipelineFactory = new RemoteSyncPipelineFactory(this);
-        bootstrap.setPipelineFactory(pipelineFactory);
         clientBootstrap = bootstrap;
     }
 
@@ -264,7 +267,7 @@ public class RemoteSyncManager extends AbstractSyncManager {
                 }
             }
         }
-        channel.write(request); 
+        channel.writeAndFlush(request); 
         return future;
     }
 
@@ -316,24 +319,24 @@ public class RemoteSyncManager extends AbstractSyncManager {
 
     protected boolean connect(String hostname, int port) {
         ready = false;
-        if (channel == null || !channel.isConnected()) {
+        if (channel == null || !channel.isActive()) {
             SocketAddress sa =
                     new InetSocketAddress(hostname, port);
             ChannelFuture future = clientBootstrap.connect(sa);
             future.awaitUninterruptibly();
             if (!future.isSuccess()) {
                 logger.error("Could not connect to " + hostname + 
-                             ":" + port, future.getCause());
+                             ":" + port, future.cause());
                 return false;
             }
-            channel = future.getChannel();
+            channel = future.channel();
         }
-        while (!ready && channel != null && channel.isConnected()) {
+        while (!ready && channel != null && channel.isActive()) {
             try {
                 Thread.sleep(10);
             } catch (InterruptedException e) { }
         }
-        if (!ready || channel == null || !channel.isConnected()) {
+        if (!ready || channel == null || !channel.isActive()) {
             logger.warn("Timed out connecting to {}:{}", hostname, port);
             return false;
         }
diff --git a/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncPipelineFactory.java b/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncPipelineFactory.java
deleted file mode 100644
index 4cf2d60bee6b1bcbe2958317d2ce0dda25530156..0000000000000000000000000000000000000000
--- a/src/main/java/org/sdnplatform/sync/internal/remote/RemoteSyncPipelineFactory.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.sdnplatform.sync.internal.remote;
-
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.channel.ChannelPipelineFactory;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.util.ExternalResourceReleasable;
-import org.jboss.netty.util.HashedWheelTimer;
-import org.jboss.netty.util.Timer;
-import org.sdnplatform.sync.internal.rpc.ThriftFrameDecoder;
-import org.sdnplatform.sync.internal.rpc.ThriftFrameEncoder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Pipeline factory for the remote sync service
- * @author readams
- */
-public class RemoteSyncPipelineFactory 
-    implements ChannelPipelineFactory, ExternalResourceReleasable {
-    protected static final Logger logger =
-            LoggerFactory.getLogger(RemoteSyncPipelineFactory.class.getName());
-    
-    protected RemoteSyncManager syncManager;
-    protected Timer timer;
-
-    private static final int maxFrameSize = 1024 * 1024 * 10;
-    
-    public RemoteSyncPipelineFactory(RemoteSyncManager syncManager) {
-        super();
-        this.syncManager = syncManager;
-        this.timer = new HashedWheelTimer();
-    }
-
-    @Override
-    public ChannelPipeline getPipeline() throws Exception {
-        RemoteSyncChannelHandler channelHandler = 
-                new RemoteSyncChannelHandler(syncManager);
-        ChannelPipeline pipeline = Channels.pipeline();
-
-        pipeline.addLast("frameDecoder",
-                         new ThriftFrameDecoder(maxFrameSize));
-        pipeline.addLast("frameEncoder",
-                         new ThriftFrameEncoder());
-        pipeline.addLast("timeout",
-                         new RSHandshakeTimeoutHandler(channelHandler,
-                                                       timer, 3));
-
-        pipeline.addLast("handler", channelHandler);
-        return pipeline;
-    }
-
-    @Override
-    public void releaseExternalResources() {
-        timer.stop();
-    }
-}
diff --git a/src/main/java/org/sdnplatform/sync/internal/rpc/AbstractRPCChannelHandler.java b/src/main/java/org/sdnplatform/sync/internal/rpc/AbstractRPCChannelHandler.java
index 8fe90ff43552e1dbec4e9e9191039bddc89c8767..9d0f2b447908b3b413a2e837a5a4a71dddd38448 100644
--- a/src/main/java/org/sdnplatform/sync/internal/rpc/AbstractRPCChannelHandler.java
+++ b/src/main/java/org/sdnplatform/sync/internal/rpc/AbstractRPCChannelHandler.java
@@ -11,15 +11,12 @@ import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
 import javax.xml.bind.DatatypeConverter;
 
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.channel.ExceptionEvent;
-import org.jboss.netty.channel.MessageEvent;
-import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
-import org.jboss.netty.handler.timeout.IdleStateEvent;
-import org.jboss.netty.handler.timeout.ReadTimeoutException;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.handler.timeout.ReadTimeoutException;
+
 import org.sdnplatform.sync.error.AuthException;
 import org.sdnplatform.sync.error.HandshakeTimeoutException;
 import org.sdnplatform.sync.error.SyncException;
@@ -61,8 +58,7 @@ import org.slf4j.LoggerFactory;
  * a {@link SyncMessage} which will provide specific type information. 
  * @author readams
  */
-public abstract class AbstractRPCChannelHandler 
-    extends IdleStateAwareChannelHandler {
+public abstract class AbstractRPCChannelHandler extends ChannelInboundHandlerAdapter {
     protected static final Logger logger =
             LoggerFactory.getLogger(AbstractRPCChannelHandler.class);
     protected String currentChallenge;
@@ -84,8 +80,7 @@ public abstract class AbstractRPCChannelHandler
     // ****************************
 
     @Override
-    public void channelConnected(ChannelHandlerContext ctx,
-                                 ChannelStateEvent e) throws Exception {
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
         channelState = ChannelState.CONNECTED;
 
         HelloMessage m = new HelloMessage();
@@ -110,12 +105,18 @@ public abstract class AbstractRPCChannelHandler
         }
         SyncMessage bsm = new SyncMessage(MessageType.HELLO);
         bsm.setHello(m);
-        ctx.getChannel().write(bsm);
+        ctx.channel().writeAndFlush(bsm);
     }
-
+    
     @Override
-    public void channelIdle(ChannelHandlerContext ctx,
-                            IdleStateEvent e) throws Exception {
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+    	if (evt instanceof IdleStateEvent) {
+    		channelIdle(ctx, (IdleStateEvent) evt);
+    	}
+    	super.userEventTriggered(ctx, evt);
+    }
+
+    public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) throws Exception {
         // send an echo request
         EchoRequestMessage m = new EchoRequestMessage();
         AsyncMessageHeader header = new AsyncMessageHeader();
@@ -123,53 +124,50 @@ public abstract class AbstractRPCChannelHandler
         m.setHeader(header);
         SyncMessage bsm = new SyncMessage(MessageType.ECHO_REQUEST);
         bsm.setEchoRequest(m);
-        ctx.getChannel().write(bsm);
+        ctx.channel().writeAndFlush(bsm);
     }
 
     @Override
-    public void exceptionCaught(ChannelHandlerContext ctx,
-                                ExceptionEvent e) throws Exception {
-        if (e.getCause() instanceof ReadTimeoutException) {
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        if (cause instanceof ReadTimeoutException) {
             // read timeout
             logger.error("[{}->{}] Disconnecting RPC node due to read timeout",
                          getLocalNodeIdString(), getRemoteNodeIdString());
-            ctx.getChannel().close();
-        } else if (e.getCause() instanceof HandshakeTimeoutException) {
+            ctx.channel().close();
+        } else if (cause instanceof HandshakeTimeoutException) {
             // read timeout
             logger.error("[{}->{}] Disconnecting RPC node due to " +
                     "handshake timeout",
                     getLocalNodeIdString(), getRemoteNodeIdString());
-            ctx.getChannel().close();
-        } else if (e.getCause() instanceof ConnectException ||
-                   e.getCause() instanceof IOException) {
+            ctx.channel().close();
+        } else if (cause instanceof ConnectException ||
+        		cause instanceof IOException) {
             logger.debug("[{}->{}] {}: {}", 
                          new Object[] {getLocalNodeIdString(),
                                        getRemoteNodeIdString(), 
-                                       e.getCause().getClass().getName(),
-                                       e.getCause().getMessage()});
+                                       cause.getClass().getName(),
+                                       cause.getMessage()});
         } else {
             logger.error("[{}->{}] An error occurred on RPC channel",
                          new Object[]{getLocalNodeIdString(), 
                                       getRemoteNodeIdString(),
-                                      e.getCause()});
-            ctx.getChannel().close();
+                                      cause});
+            ctx.channel().close();
         }
     }
 
     @Override
-    public void messageReceived(ChannelHandlerContext ctx,
-                                MessageEvent e) throws Exception {
-        Object message = e.getMessage();
+    public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception {
         if (message instanceof SyncMessage) {
-            handleSyncMessage((SyncMessage)message, ctx.getChannel());
+            handleSyncMessage((SyncMessage)message, ctx.channel());
         } else if (message instanceof List) {
             for (Object i : (List<?>)message) {
                 if (i instanceof SyncMessage) {
                     try {
                         handleSyncMessage((SyncMessage)i,
-                                             ctx.getChannel());
+                                             ctx.channel());
                     } catch (Exception ex) {
-                        Channels.fireExceptionCaught(ctx, ex);
+                        ctx.fireExceptionCaught(ex);
                     }
                 }
             }
@@ -323,7 +321,7 @@ public abstract class AbstractRPCChannelHandler
                         new Object[]{getLocalNodeIdString(), 
                                      getRemoteNodeIdString(), 
                                      e.getMessage()});
-            channel.write(getError(request.getHeader().getTransactionId(), 
+            channel.writeAndFlush(getError(request.getHeader().getTransactionId(), 
                                    e, MessageType.HELLO));
             channel.close();
         }
@@ -355,7 +353,7 @@ public abstract class AbstractRPCChannelHandler
             AuthChallengeResponse reply = new AuthChallengeResponse();
             reply.setResponse(generateResponse(cr.getChallenge()));
             m.setAuthChallengeResponse(reply);
-            channel.write(bsm);
+            channel.writeAndFlush(bsm);
         } else {
             throw new AuthException("No authentication data in " + 
                     "handshake message");
@@ -382,7 +380,7 @@ public abstract class AbstractRPCChannelHandler
         m.setHeader(header);
         SyncMessage bsm = new SyncMessage(MessageType.ECHO_REPLY);
         bsm.setEchoReply(m);
-        channel.write(bsm);
+        channel.writeAndFlush(bsm);
     }
 
     protected void handleGetRequest(GetRequestMessage request,
@@ -548,7 +546,7 @@ public abstract class AbstractRPCChannelHandler
                     new Object[]{getLocalNodeIdString(), 
                                  getRemoteNodeIdString(),
                                  message});
-        channel.write(getError(transactionId, 
+        channel.writeAndFlush(getError(transactionId, 
                                new SyncException(message), type));
     }
     
diff --git a/src/main/java/org/sdnplatform/sync/internal/rpc/HandshakeTimeoutHandler.java b/src/main/java/org/sdnplatform/sync/internal/rpc/HandshakeTimeoutHandler.java
index 2ba0e4bb9be944b6729ee23cf0bf31b70040e4d0..7fc9b7f9a59fa3d7f4fef2cdb35d6eafdb7809ee 100644
--- a/src/main/java/org/sdnplatform/sync/internal/rpc/HandshakeTimeoutHandler.java
+++ b/src/main/java/org/sdnplatform/sync/internal/rpc/HandshakeTimeoutHandler.java
@@ -19,23 +19,18 @@ package org.sdnplatform.sync.internal.rpc;
 
 import java.util.concurrent.TimeUnit;
 
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
-import org.jboss.netty.util.ExternalResourceReleasable;
-import org.jboss.netty.util.Timeout;
-import org.jboss.netty.util.Timer;
-import org.jboss.netty.util.TimerTask;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
 import org.sdnplatform.sync.error.HandshakeTimeoutException;
 
 
 /**
  * Trigger a timeout if a switch fails to complete handshake soon enough
  */
-public class HandshakeTimeoutHandler 
-    extends SimpleChannelUpstreamHandler
-    implements ExternalResourceReleasable {
+public class HandshakeTimeoutHandler extends ChannelInboundHandlerAdapter {
     static final HandshakeTimeoutException EXCEPTION = 
             new HandshakeTimeoutException();
     
@@ -55,27 +50,23 @@ public class HandshakeTimeoutHandler
     }
     
     @Override
-    public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
+    public void channelActive(ChannelHandlerContext ctx)
             throws Exception {
         if (timeoutNanos > 0) {
             timeout = timer.newTimeout(new HandshakeTimeoutTask(ctx), 
                                        timeoutNanos, TimeUnit.NANOSECONDS);
         }
-        ctx.sendUpstream(e);
+        ctx.fireChannelActive();
     }
     
     @Override
-    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
+    public void channelInactive(ChannelHandlerContext ctx)
             throws Exception {
         if (timeout != null) {
             timeout.cancel();
             timeout = null;
         }
-    }
-
-    @Override
-    public void releaseExternalResources() {
-        timer.stop();
+        ctx.fireChannelInactive();
     }
     
     private final class HandshakeTimeoutTask implements TimerTask {
@@ -92,14 +83,14 @@ public class HandshakeTimeoutHandler
                 return;
             }
 
-            if (!ctx.getChannel().isOpen()) {
+            if (!ctx.channel().isOpen()) {
                 return;
             }
             if (!handler.isClientConnection && 
                 ((handler.remoteNode == null ||
                  !handler.rpcService.isConnected(handler.remoteNode.
                                                  getNodeId()))))
-                Channels.fireExceptionCaught(ctx, EXCEPTION);
+                ctx.fireExceptionCaught(EXCEPTION);
         }
     }
 }
diff --git a/src/main/java/org/sdnplatform/sync/internal/rpc/RPCChannelHandler.java b/src/main/java/org/sdnplatform/sync/internal/rpc/RPCChannelHandler.java
index bb7f942cf14abf72bd7aefba05b6d46291f3756a..a1c4fd7af7e73f9f97f5d165866adff4cca9fd10 100644
--- a/src/main/java/org/sdnplatform/sync/internal/rpc/RPCChannelHandler.java
+++ b/src/main/java/org/sdnplatform/sync/internal/rpc/RPCChannelHandler.java
@@ -8,10 +8,9 @@ import java.util.Map.Entry;
 
 import net.floodlightcontroller.debugcounter.IDebugCounter;
 
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.MessageEvent;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+
 import org.sdnplatform.sync.IClosableIterator;
 import org.sdnplatform.sync.IStoreClient;
 import org.sdnplatform.sync.IVersion;
@@ -61,29 +60,23 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
     // ****************************
 
     @Override
-    public void channelOpen(ChannelHandlerContext ctx,
-                            ChannelStateEvent e) throws Exception {
-        rpcService.cg.add(ctx.getChannel());
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        rpcService.getChannelGroup().add(ctx.channel());
+        super.channelActive(ctx);
     }
 
     @Override
-    public void channelDisconnected(ChannelHandlerContext ctx,
-                                    ChannelStateEvent e) throws Exception {
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
         if (remoteNode != null) {
             rpcService.disconnectNode(remoteNode.getNodeId());
         }
+        super.channelInactive(ctx);
     }
 
     // ******************************************
     // AbstractRPCChannelHandler message handlers
     // ******************************************
 
-    @Override
-    public void messageReceived(ChannelHandlerContext ctx,
-                                MessageEvent e) throws Exception {
-        super.messageReceived(ctx, e);
-    }
-
     @Override
     protected void handleHello(HelloMessage hello, Channel channel) {
         if (!hello.isSetNodeId()) {
@@ -109,7 +102,7 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
         header.setTransactionId(getTransactionId());
         srm.setHeader(header);
         SyncMessage bsm = new SyncMessage(MessageType.FULL_SYNC_REQUEST);
-        channel.write(bsm);
+        channel.writeAndFlush(bsm);
 
         // XXX - TODO - if last connection was longer ago than the tombstone
         // timeout, then we need to do a complete flush and reload of our
@@ -142,9 +135,9 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
 
             SyncMessage bsm = new SyncMessage(MessageType.GET_RESPONSE);
             bsm.setGetResponse(m);
-            channel.write(bsm);
+            channel.writeAndFlush(bsm);
         } catch (Exception e) {
-            channel.write(getError(request.getHeader().getTransactionId(), e,
+            channel.writeAndFlush(getError(request.getHeader().getTransactionId(), e,
                                    MessageType.GET_REQUEST));
         }
     }
@@ -187,9 +180,9 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
 
             SyncMessage bsm = new SyncMessage(MessageType.PUT_RESPONSE);
             bsm.setPutResponse(m);
-            channel.write(bsm);
+            channel.writeAndFlush(bsm);
         } catch (Exception e) {
-            channel.write(getError(request.getHeader().getTransactionId(), e,
+            channel.writeAndFlush(getError(request.getHeader().getTransactionId(), e,
                                    MessageType.PUT_REQUEST));
         }
     }
@@ -226,9 +219,9 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
             SyncMessage bsm =
                     new SyncMessage(MessageType.DELETE_RESPONSE);
             bsm.setDeleteResponse(m);
-            channel.write(bsm);
+            channel.writeAndFlush(bsm);
         } catch (Exception e) {
-            channel.write(getError(request.getHeader().getTransactionId(), e,
+            channel.writeAndFlush(getError(request.getHeader().getTransactionId(), e,
                                    MessageType.DELETE_REQUEST));
         }
     }
@@ -268,9 +261,9 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
 
             updateCounter(SyncManager.counterReceivedValues,
                           request.getValuesSize());
-            channel.write(bsm);
+            channel.writeAndFlush(bsm);
         } catch (Exception e) {
-            channel.write(getError(request.getHeader().getTransactionId(), e,
+            channel.writeAndFlush(getError(request.getHeader().getTransactionId(), e,
                                    MessageType.SYNC_VALUE));
         }
     }
@@ -313,10 +306,10 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
                                           getRemoteNodeIdString(),
                                           srm.getKeysSize()});
             }
-            channel.write(bsm);
+            channel.writeAndFlush(bsm);
 
         } catch (Exception e) {
-            channel.write(getError(request.getHeader().getTransactionId(),
+            channel.writeAndFlush(getError(request.getHeader().getTransactionId(),
                                    e, MessageType.SYNC_OFFER));
         }
     }
@@ -355,7 +348,7 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
                                                          bsm));
             }
         } catch (Exception e) {
-            channel.write(getError(request.getHeader().getTransactionId(), e,
+            channel.writeAndFlush(getError(request.getHeader().getTransactionId(), e,
                                    MessageType.SYNC_REQUEST));
         }
     }
@@ -402,9 +395,9 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
             SyncMessage bsm =
                     new SyncMessage(MessageType.CURSOR_RESPONSE);
             bsm.setCursorResponse(m);
-            channel.write(bsm);
+            channel.writeAndFlush(bsm);
         } catch (Exception e) {
-            channel.write(getError(request.getHeader().getTransactionId(),
+            channel.writeAndFlush(getError(request.getHeader().getTransactionId(),
                                    e, MessageType.CURSOR_REQUEST));
         }
     }
@@ -426,9 +419,9 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
             SyncMessage bsm =
                     new SyncMessage(MessageType.REGISTER_RESPONSE);
             bsm.setRegisterResponse(m);
-            channel.write(bsm);
+            channel.writeAndFlush(bsm);
         } catch (Exception e) {
-            channel.write(getError(request.getHeader().getTransactionId(), e,
+            channel.writeAndFlush(getError(request.getHeader().getTransactionId(), e,
                                    MessageType.REGISTER_REQUEST));
         }
     }
@@ -511,9 +504,9 @@ public class RPCChannelHandler extends AbstractRPCChannelHandler {
             SyncMessage bsm =
                     new SyncMessage(MessageType.CLUSTER_JOIN_RESPONSE);
             bsm.setClusterJoinResponse(cjrm);
-            channel.write(bsm);
+            channel.writeAndFlush(bsm);
         } catch (Exception e) {
-            channel.write(getError(request.getHeader().getTransactionId(), e,
+            channel.writeAndFlush(getError(request.getHeader().getTransactionId(), e,
                                    MessageType.CLUSTER_JOIN_REQUEST));
         }
     }
diff --git a/src/main/java/org/sdnplatform/sync/internal/rpc/RPCChannelInitializer.java b/src/main/java/org/sdnplatform/sync/internal/rpc/RPCChannelInitializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..28b96de13d119aa23146c7ca5b6c55023d8c15ef
--- /dev/null
+++ b/src/main/java/org/sdnplatform/sync/internal/rpc/RPCChannelInitializer.java
@@ -0,0 +1,58 @@
+package org.sdnplatform.sync.internal.rpc;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.handler.timeout.ReadTimeoutHandler;
+import io.netty.util.Timer;
+
+import org.sdnplatform.sync.internal.SyncManager;
+
+
+/**
+ * Pipeline factory for the sync service.
+ * @see SyncManager
+ * @author readams
+ */
+public class RPCChannelInitializer extends ChannelInitializer<Channel> {
+
+    protected SyncManager syncManager;
+    protected RPCService rpcService;
+    protected Timer timer;
+
+    private static final int maxFrameSize = 512 * 1024;
+    
+    public RPCChannelInitializer(SyncManager syncManager,
+                              RPCService rpcService,
+                              Timer timer) {
+        super();
+        this.syncManager = syncManager;
+        this.rpcService = rpcService;
+        this.timer = timer;
+    }
+
+    @Override
+    protected void initChannel(Channel ch) throws Exception {
+        RPCChannelHandler channelHandler = 
+                new RPCChannelHandler(syncManager, rpcService);
+
+        IdleStateHandler idleHandler = 
+                new IdleStateHandler(5, 10, 0);
+        ReadTimeoutHandler readTimeoutHandler = 
+                new ReadTimeoutHandler(30);
+        
+        ChannelPipeline pipeline = ch.pipeline();
+        pipeline.addLast("idle", idleHandler);
+        pipeline.addLast("timeout", readTimeoutHandler);
+        pipeline.addLast("handshaketimeout",
+                         new HandshakeTimeoutHandler(channelHandler, timer, 10));
+
+        pipeline.addLast("syncMessageDecoder",
+                         new SyncMessageDecoder(maxFrameSize));
+        pipeline.addLast("syncMessageEncoder",
+                         new SyncMessageEncoder());
+
+        pipeline.addLast("handler", channelHandler);
+    }
+}
diff --git a/src/main/java/org/sdnplatform/sync/internal/rpc/RPCPipelineFactory.java b/src/main/java/org/sdnplatform/sync/internal/rpc/RPCPipelineFactory.java
deleted file mode 100644
index 4016711e57d8fe851f8a6aa5ad1288974f265e37..0000000000000000000000000000000000000000
--- a/src/main/java/org/sdnplatform/sync/internal/rpc/RPCPipelineFactory.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.sdnplatform.sync.internal.rpc;
-
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.channel.ChannelPipelineFactory;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.handler.timeout.IdleStateHandler;
-import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
-import org.jboss.netty.util.ExternalResourceReleasable;
-import org.jboss.netty.util.HashedWheelTimer;
-import org.jboss.netty.util.Timer;
-import org.sdnplatform.sync.internal.SyncManager;
-
-
-/**
- * Pipeline factory for the sync service.
- * @see SyncManager
- * @author readams
- */
-public class RPCPipelineFactory 
-    implements ChannelPipelineFactory, ExternalResourceReleasable {
-
-    protected SyncManager syncManager;
-    protected RPCService rpcService;
-    protected Timer timer;
-
-    private static final int maxFrameSize = 512 * 1024;
-    
-    public RPCPipelineFactory(SyncManager syncManager,
-                              RPCService rpcService) {
-        super();
-        this.syncManager = syncManager;
-        this.rpcService = rpcService;
-
-        this.timer = new HashedWheelTimer();
-    }
-
-    @Override
-    public ChannelPipeline getPipeline() throws Exception {
-        RPCChannelHandler channelHandler = 
-                new RPCChannelHandler(syncManager, rpcService);
-
-        IdleStateHandler idleHandler = 
-                new IdleStateHandler(timer, 5, 10, 0);
-        ReadTimeoutHandler readTimeoutHandler = 
-                new ReadTimeoutHandler(timer, 30);
-        
-        ChannelPipeline pipeline = Channels.pipeline();
-        pipeline.addLast("idle", idleHandler);
-        pipeline.addLast("timeout", readTimeoutHandler);
-        pipeline.addLast("handshaketimeout",
-                         new HandshakeTimeoutHandler(channelHandler, timer, 10));
-
-        pipeline.addLast("frameDecoder",
-                         new ThriftFrameDecoder(maxFrameSize));
-        pipeline.addLast("frameEncoder",
-                         new ThriftFrameEncoder());
-
-        pipeline.addLast("handler", channelHandler);
-        return pipeline;
-    }
-
-    @Override
-    public void releaseExternalResources() {
-        timer.stop();        
-    }
-}
diff --git a/src/main/java/org/sdnplatform/sync/internal/rpc/RPCService.java b/src/main/java/org/sdnplatform/sync/internal/rpc/RPCService.java
index 60dbcbbc8e9089476db022de386e70c4bd51162f..962e355437fd946335f5f56c7a2c95977ca75670 100644
--- a/src/main/java/org/sdnplatform/sync/internal/rpc/RPCService.java
+++ b/src/main/java/org/sdnplatform/sync/internal/rpc/RPCService.java
@@ -18,17 +18,21 @@ import java.util.concurrent.LinkedTransferQueue;
 
 import net.floodlightcontroller.core.util.SingletonTask;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.GlobalEventExecutor;
 
-import org.jboss.netty.bootstrap.ClientBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelFuture;
-import org.jboss.netty.channel.ChannelFutureListener;
-import org.jboss.netty.channel.ChannelPipelineFactory;
-import org.jboss.netty.channel.group.ChannelGroup;
-import org.jboss.netty.channel.group.DefaultChannelGroup;
-import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
-import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
 import org.sdnplatform.sync.internal.SyncManager;
 import org.sdnplatform.sync.internal.config.Node;
 import org.sdnplatform.sync.internal.util.Pair;
@@ -58,32 +62,27 @@ public class RPCService {
     /**
      * Channel group that will hold all our channels
      */
-    final ChannelGroup cg = new DefaultChannelGroup("Internal RPC");
+    private final ChannelGroup cg = new DefaultChannelGroup("Internal RPC", GlobalEventExecutor.INSTANCE);
     
     /**
-     * {@link ExecutorService} used for netty boss threads
+     * {@link EventLoopGroup} used for netty boss threads
      */
-    protected ExecutorService bossExecutor;
+    protected EventLoopGroup bossGroup;
     
     /**
-     * {@link ExecutorService} used for netty worker threads
+     * {@link EventLoopGroup} used for netty worker threads
      */
-    protected ExecutorService workerExecutor;
+    protected EventLoopGroup workerGroup;
 
     /**
      * Netty {@link ClientBootstrap} used for creating client connections 
      */
-    protected ClientBootstrap clientBootstrap;
-    
-    /**
-     * Netty {@link ServerBootstrap} used for creating server connections 
-     */
-    protected ServerBootstrap serverBootstrap;
+    protected Bootstrap clientBootstrap;
 
     /**
-     * {@link ChannelPipelineFactory} for creating connections 
+     * {@link RPCChannelInitializer} for creating connections 
      */
-    protected RPCPipelineFactory pipelineFactory;
+    protected RPCChannelInitializer channelInitializer;
 
     /**
      * Node connections
@@ -122,6 +121,11 @@ public class RPCService {
      */
     protected SingletonTask reconnectTask;
     
+    /**
+     * Timer used for timeouts
+     */
+    private final Timer timer;
+    
     /**
      * If we want to rate-limit certain types of messages, we can do
      * so by limiting the overall number of outstanding messages.  
@@ -162,10 +166,12 @@ public class RPCService {
     protected static final int MAX_PENDING_MESSAGES = 500;
 
     public RPCService(SyncManager syncManager, 
-                      IDebugCounterService debugCounter) {
+                      IDebugCounterService debugCounter,
+                      Timer timer) {
         super();
         this.syncManager = syncManager;
         this.debugCounter = debugCounter;
+        this.timer = timer;
 
         messageWindows = new ConcurrentHashMap<Short, MessageWindow>();
     }
@@ -205,13 +211,13 @@ public class RPCService {
             }
         };
         
-        bossExecutor = Executors.newCachedThreadPool(f2);
-        workerExecutor = Executors.newCachedThreadPool(f2);
+        bossGroup = new NioEventLoopGroup(0, f2);
+        workerGroup = new NioEventLoopGroup(0, f2);
 
-        pipelineFactory = new RPCPipelineFactory(syncManager, this);
+        channelInitializer = new RPCChannelInitializer(syncManager, this, timer);
 
-        startServer(pipelineFactory);
-        startClients(pipelineFactory);
+        startServer(channelInitializer);
+        startClients(channelInitializer);
     }
 
     /**
@@ -224,21 +230,15 @@ public class RPCService {
                 logger.warn("Failed to cleanly shut down RPC server");
                 return;
             }
-            if (clientBootstrap != null)
-                clientBootstrap.releaseExternalResources();
+
             clientBootstrap = null;
-            if (serverBootstrap != null)
-                serverBootstrap.releaseExternalResources();
-            serverBootstrap = null;
-            if (pipelineFactory != null)
-                pipelineFactory.releaseExternalResources();
-            pipelineFactory = null;
-            if (bossExecutor != null)
-                bossExecutor.shutdown();
-            bossExecutor = null;
-            if (workerExecutor != null)
-                workerExecutor.shutdown();
-            workerExecutor = null;
+            channelInitializer = null;
+            if (bossGroup != null)
+            	bossGroup.shutdownGracefully();
+            bossGroup = null;
+            if (workerGroup != null)
+            	workerGroup.shutdownGracefully();
+            workerGroup = null;
         } catch (InterruptedException e) {
             logger.warn("Interrupted while shutting down RPC server");
         }
@@ -268,7 +268,7 @@ public class RPCService {
         NodeConnection nc = connections.get(nodeId);
         if (nc != null && nc.state == NodeConnectionState.CONNECTED) {
             waitForMessageWindow(bsm.getType(), nodeId, 0);
-            nc.nodeChannel.write(bsm);
+            nc.nodeChannel.writeAndFlush(bsm);
             return true;
         }
         return false;
@@ -422,19 +422,17 @@ public class RPCService {
     /**
      * Start listening sockets
      */
-    protected void startServer(ChannelPipelineFactory pipelineFactory) {
-        final ServerBootstrap bootstrap =
-                new ServerBootstrap(
-                     new NioServerSocketChannelFactory(bossExecutor,
-                                                       workerExecutor));
-        bootstrap.setOption("reuseAddr", true);
-        bootstrap.setOption("child.keepAlive", true);
-        bootstrap.setOption("child.tcpNoDelay", true);
-        bootstrap.setOption("child.sendBufferSize", SEND_BUFFER_SIZE);
-        bootstrap.setOption("child.receiveBufferSize", SEND_BUFFER_SIZE);
-
-        bootstrap.setPipelineFactory(pipelineFactory);
-        serverBootstrap = bootstrap;
+    protected void startServer(RPCChannelInitializer channelInitializer) {
+        final ServerBootstrap bootstrap = new ServerBootstrap();
+        bootstrap.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, SEND_BUFFER_SIZE)
+        .option(ChannelOption.SO_RCVBUF, SEND_BUFFER_SIZE)
+        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECT_TIMEOUT)
+        .childHandler(channelInitializer);
 
         int port = syncManager.getClusterConfig().getNode().getPort();
         InetSocketAddress sa;
@@ -445,8 +443,9 @@ public class RPCService {
         else
             sa = new InetSocketAddress(port);
 
-        cg.add(bootstrap.bind(sa));
-
+        ChannelFuture bindFuture = bootstrap.bind(sa);
+        cg.add(bindFuture.channel());
+        
         logger.info("Listening for internal floodlight RPC on {}", sa);
     }
 
@@ -468,12 +467,12 @@ public class RPCService {
                 synchronized (connections) {
                     NodeConnection c = connections.remove(node.getNodeId());
                     if (c != null) c.nuke();
-                    cf.getChannel().close();
+                    cf.channel().close();
                 }
                 
                 String message = "[unknown error]";
                 if (cf.isCancelled()) message = "Timed out on connect";
-                if (cf.getCause() != null) message = cf.getCause().getMessage();
+                if (cf.cause() != null) message = cf.cause().getMessage();
                 logger.debug("[{}->{}] Could not connect to RPC " +
                              "node: {}", 
                              new Object[]{syncManager.getLocalNodeId(), 
@@ -511,17 +510,16 @@ public class RPCService {
      * any nodes with a lower ID so that there will be a single connection
      * between each pair of nodes which we'll use symmetrically
      */
-    protected void startClients(ChannelPipelineFactory pipelineFactory) {
-        final ClientBootstrap bootstrap =
-                new ClientBootstrap(
-                     new NioClientSocketChannelFactory(bossExecutor,
-                                                       workerExecutor));
-        bootstrap.setOption("child.reuseAddr", true);
-        bootstrap.setOption("child.keepAlive", true);
-        bootstrap.setOption("child.tcpNoDelay", true);
-        bootstrap.setOption("child.sendBufferSize", SEND_BUFFER_SIZE);
-        bootstrap.setOption("child.connectTimeoutMillis", CONNECT_TIMEOUT);
-        bootstrap.setPipelineFactory(pipelineFactory);
+    protected void startClients(RPCChannelInitializer channelInitializer) {
+        final Bootstrap bootstrap = new Bootstrap();
+        bootstrap.group(workerGroup)
+        .channel(NioSocketChannel.class)
+        .option(ChannelOption.SO_REUSEADDR, true)
+        .option(ChannelOption.SO_KEEPALIVE, true)
+        .option(ChannelOption.TCP_NODELAY, true)
+        .option(ChannelOption.SO_SNDBUF, SEND_BUFFER_SIZE)
+        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECT_TIMEOUT)
+        .handler(channelInitializer);
         clientBootstrap = bootstrap;
 
         ScheduledExecutorService ses = 
@@ -576,6 +574,14 @@ public class RPCService {
             doNodeConnect(n);
         }        
     }
+    
+    /**
+     * Retrieve the Netty ChannelGroup
+     * @return
+     */
+    protected ChannelGroup getChannelGroup() {
+    	return cg;
+    }
 
     /**
      * Periodically ensure that all the node connections are alive
@@ -617,7 +623,7 @@ public class RPCService {
         
         protected void nuke() {
             state = NodeConnectionState.NONE;
-            if (pendingFuture != null) pendingFuture.cancel();
+            if (pendingFuture != null) pendingFuture.cancel(false);
             if (nodeChannel != null) nodeChannel.close();
             pendingFuture = null;
             nodeChannel = null;
@@ -668,4 +674,4 @@ public class RPCService {
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/sdnplatform/sync/internal/rpc/SyncMessageDecoder.java b/src/main/java/org/sdnplatform/sync/internal/rpc/SyncMessageDecoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5ac20eb50bbaec865048c9050b6c86c66ac6e6e
--- /dev/null
+++ b/src/main/java/org/sdnplatform/sync/internal/rpc/SyncMessageDecoder.java
@@ -0,0 +1,18 @@
+package org.sdnplatform.sync.internal.rpc;
+
+import net.floodlightcontroller.core.util.ThriftFrameDecoder;
+
+import org.sdnplatform.sync.thrift.SyncMessage;
+
+public class SyncMessageDecoder extends ThriftFrameDecoder<SyncMessage> {
+
+    public SyncMessageDecoder(int maxSize) {
+        super(maxSize);
+    }
+
+    @Override
+    protected SyncMessage allocateMessage() {
+        return new SyncMessage();
+    }
+
+}
diff --git a/src/main/java/org/sdnplatform/sync/internal/rpc/SyncMessageEncoder.java b/src/main/java/org/sdnplatform/sync/internal/rpc/SyncMessageEncoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..21e625f097b06b80721d0ea8b730c3527debb9ce
--- /dev/null
+++ b/src/main/java/org/sdnplatform/sync/internal/rpc/SyncMessageEncoder.java
@@ -0,0 +1,9 @@
+package org.sdnplatform.sync.internal.rpc;
+
+import org.sdnplatform.sync.thrift.SyncMessage;
+
+import net.floodlightcontroller.core.util.ThriftFrameEncoder;
+
+public class SyncMessageEncoder extends ThriftFrameEncoder<SyncMessage> {
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/sdnplatform/sync/internal/rpc/ThriftFrameDecoder.java b/src/main/java/org/sdnplatform/sync/internal/rpc/ThriftFrameDecoder.java
deleted file mode 100644
index 50ec9d0f81f6613ba33555ed7366634baac2f759..0000000000000000000000000000000000000000
--- a/src/main/java/org/sdnplatform/sync/internal/rpc/ThriftFrameDecoder.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.sdnplatform.sync.internal.rpc;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.thrift.protocol.TCompactProtocol;
-import org.apache.thrift.transport.TIOStreamTransport;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBufferInputStream;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.sdnplatform.sync.thrift.SyncMessage;
-
-/**
- * Decode a {@link SyncMessage} from the channel
- * @author readams
- */
-public class ThriftFrameDecoder extends LengthFieldBasedFrameDecoder {
-
-    public ThriftFrameDecoder(int maxSize) {
-        super(maxSize, 0, 4, 0, 4);
-    }
-
-    @Override
-    protected Object decode(ChannelHandlerContext ctx,
-                            Channel channel,
-                            ChannelBuffer buffer) throws Exception {
-        List<SyncMessage> ms = null;
-        ChannelBuffer frame = null;
-        while (null != (frame = (ChannelBuffer) super.decode(ctx, channel, 
-                                                             buffer))) {
-            if (ms == null) ms = new ArrayList<SyncMessage>();
-            ChannelBufferInputStream is = new ChannelBufferInputStream(frame);
-            TCompactProtocol thriftProtocol =
-                    new TCompactProtocol(new TIOStreamTransport(is));
-            SyncMessage bsm = new SyncMessage();
-            bsm.read(thriftProtocol);
-            ms.add(bsm);
-        }
-        return ms;
-    }
-
-    @Override
-    protected ChannelBuffer extractFrame(ChannelBuffer buffer,
-                                         int index, int length) {
-        return buffer.slice(index, length);
-    }
-}
diff --git a/src/main/java/org/sdnplatform/sync/internal/rpc/ThriftFrameEncoder.java b/src/main/java/org/sdnplatform/sync/internal/rpc/ThriftFrameEncoder.java
deleted file mode 100644
index d71e8d2de8e51dd0285fa55b85c3d6e6c75bcc4e..0000000000000000000000000000000000000000
--- a/src/main/java/org/sdnplatform/sync/internal/rpc/ThriftFrameEncoder.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.sdnplatform.sync.internal.rpc;
-
-import org.apache.thrift.protocol.TCompactProtocol;
-import org.apache.thrift.transport.TIOStreamTransport;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBufferOutputStream;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.buffer.DynamicChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
-import org.sdnplatform.sync.thrift.SyncMessage;
-
-
-/**
- * Encode a {@link SyncMessage} into the channel
- * @author readams
- *
- */
-public class ThriftFrameEncoder extends OneToOneEncoder {
-
-    @Override
-    protected Object encode(ChannelHandlerContext ctx, Channel channel,
-                            Object message) throws Exception {
-        if (message instanceof SyncMessage) {
-            ChannelBuffer buf = new DynamicChannelBuffer(512);
-            ChannelBufferOutputStream os = new ChannelBufferOutputStream(buf);
-            TCompactProtocol thriftProtocol =
-                    new TCompactProtocol(new TIOStreamTransport(os));
-            ((SyncMessage) message).write(thriftProtocol);
-
-            ChannelBuffer len = ChannelBuffers.buffer(4);
-            len.writeInt(buf.readableBytes());
-            return ChannelBuffers.wrappedBuffer(len, buf);
-        }
-        return message;
-    }
-
-}
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 e11880981126a0cdae3c604e5c9f4a0783f9db5c..b7dfe953e276c4fea382bdb2bd1ff63851d52f74 100644
--- a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
+++ b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
@@ -26,4 +26,5 @@ net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl
 net.floodlightcontroller.firewall.Firewall
 net.floodlightcontroller.accesscontrollist.ACL
 net.floodlightcontroller.dhcpserver.DHCPServer
-net.floodlightcontroller.learningswitch.LearningSwitch
\ No newline at end of file
+net.floodlightcontroller.learningswitch.LearningSwitch
+net.floodlightcontroller.statistics.StatisticsCollector
\ No newline at end of file
diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties
index 5b909f849c42df0dfeb5012b465aaaa7fa3cd27f..89bf32381355331dd93a011d0e59d2a2581d4d5d 100644
--- a/src/main/resources/floodlightdefault.properties
+++ b/src/main/resources/floodlightdefault.properties
@@ -15,18 +15,19 @@ net.floodlightcontroller.ui.web.StaticWebRoutable,\
 net.floodlightcontroller.loadbalancer.LoadBalancer,\
 net.floodlightcontroller.firewall.Firewall,\
 net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl,\
-net.floodlightcontroller.accesscontrollist.ACL
+net.floodlightcontroller.accesscontrollist.ACL,\
+net.floodlightcontroller.statistics.StatisticsCollector
 org.sdnplatform.sync.internal.SyncManager.authScheme=CHALLENGE_RESPONSE
 org.sdnplatform.sync.internal.SyncManager.keyStorePath=/etc/floodlight/auth_credentials.jceks
 org.sdnplatform.sync.internal.SyncManager.dbPath=/var/lib/floodlight/
 org.sdnplatform.sync.internal.SyncManager.port=6642
 net.floodlightcontroller.forwarding.Forwarding.match=vlan, mac, ip, transport
 net.floodlightcontroller.forwarding.Forwarding.flood-arp=NO
-net.floodlightcontroller.core.internal.FloodlightProvider.openflowPort=6653
+net.floodlightcontroller.core.internal.FloodlightProvider.openFlowPort=6653
 net.floodlightcontroller.core.internal.FloodlightProvider.role=ACTIVE
 net.floodlightcontroller.linkdiscovery.internal.LinkDiscoveryManager.latency-history-size=10
 net.floodlightcontroller.linkdiscovery.internal.LinkDiscoveryManager.latency-update-threshold=0.5
-net.floodlightcontroller.core.internal.OFSwitchManager.defaultMaxTablesToReceiveTableMissFlow=2
+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
@@ -41,3 +42,5 @@ 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.statistics.StatisticsCollector.enable=FALSE
+net.floodlightcontroller.statistics.StatisticsCollector.collectionIntervalPortStatsSeconds=10
\ No newline at end of file
diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml
index b5e3bfd8093df991c049e5f86e873a9834d12747..8fa1e8f86065f20fa51ea0b42336af21b4e15cdc 100644
--- a/src/main/resources/logback-test.xml
+++ b/src/main/resources/logback-test.xml
@@ -11,6 +11,7 @@
     <appender-ref ref="EV_WARN_ERR" />
   </root>
   <logger name="org" level="WARN"/>
+  <logger name="io" level="INFO"></logger> <!-- Netty logging -->
   <logger name="LogService" level="DEBUG"></logger> <!-- Restlet access logging -->
   <logger name="net.floodlightcontroller" level="INFO"/>
   <logger name="org.sdnplatform" level="INFO"></logger>
diff --git a/src/test/java/net/floodlightcontroller/core/internal/MockOFConnection.java b/src/test/java/net/floodlightcontroller/core/internal/MockOFConnection.java
index c1eab8ba8baedd748aaddeb089504befb4cad6a0..ccf19f5b057e6f019803f8bab4a35e82bcda9317 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/MockOFConnection.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/MockOFConnection.java
@@ -4,6 +4,8 @@ import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -72,13 +74,15 @@ public class MockOFConnection implements IOFConnectionBackend {
     }
 
     @Override
-    public void write(OFMessage m) {
+    public boolean write(OFMessage m) {
         messages.add(m);
+        return true;
     }
 
     @Override
-    public void write(Iterable<OFMessage> msglist) {
+    public Collection<OFMessage> write(Iterable<OFMessage> msglist) {
         Iterables.addAll(messages, msglist);
+        return Collections.emptyList();
     }
 
     static class RequestAndFuture<R extends OFMessage> {
@@ -124,11 +128,6 @@ public class MockOFConnection implements IOFConnectionBackend {
         return this.connectedSince;
     }
 
-    @Override
-    public void flush() {
-        // no op
-    }
-
     @Override
     public DatapathId getDatapathId() {
         return this.id;
diff --git a/src/test/java/net/floodlightcontroller/core/internal/MockOFSwitchImpl.java b/src/test/java/net/floodlightcontroller/core/internal/MockOFSwitchImpl.java
index 846f4f7b82ad89a557f6b30aaf0b1ddacd6f1675..0fcbc88c19b217e6d67141acc9f44b5f093c8046 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/MockOFSwitchImpl.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/MockOFSwitchImpl.java
@@ -7,7 +7,6 @@ import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import org.easymock.EasyMock;
-import net.floodlightcontroller.core.OFSwitch;
 import org.projectfloodlight.openflow.protocol.OFCapabilities;
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFStatsReply;
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java
index 96147b02eb52b4323be4f804ffad6014c552809b..39ec2714e9aa9170a2f115f46cef7533e6593af8 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java
@@ -23,23 +23,25 @@ import java.util.Set;
 import org.easymock.Capture;
 import org.easymock.CaptureType;
 import org.easymock.EasyMock;
+import org.easymock.IAnswer;
 import org.hamcrest.CoreMatchers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.ExceptionEvent;
-import org.jboss.netty.channel.MessageEvent;
-import org.jboss.netty.util.HashedWheelTimer;
-import org.jboss.netty.util.Timer;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.DefaultChannelPromise;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import net.floodlightcontroller.core.IOFConnectionBackend;
-import net.floodlightcontroller.core.OFConnectionCounters;
-import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandler;
-import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandshakeTimeout;
+import net.floodlightcontroller.core.internal.OFChannelInitializer.PipelineHandler;
+import net.floodlightcontroller.core.internal.OFChannelInitializer.PipelineHandshakeTimeout;
+import net.floodlightcontroller.core.test.TestEventLoop;
 import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 
@@ -67,6 +69,7 @@ import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.U32;
 
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 
 
@@ -78,13 +81,10 @@ public class OFChannelHandlerVer10Test {
     private Channel channel;
     private Timer timer;
     private ChannelHandlerContext ctx;
-    private MessageEvent messageEvent;
-    private ChannelStateEvent channelStateEvent;
     private ChannelPipeline pipeline;
-    // FIXME:LOJI: Currently only use OF 1.0
     private final OFFactory factory = OFFactories.getFactory(OFVersion.OF_10);
 
-    private Capture<ExceptionEvent> exceptionEventCapture;
+    private Capture<Throwable> exceptionEventCapture;
     private Capture<List<OFMessage>> writeCapture;
 
     private OFPortDesc portDesc;
@@ -94,6 +94,8 @@ public class OFChannelHandlerVer10Test {
     private INewOFConnectionListener newConnectionListener;
     private Capture<IOFConnectionBackend> newConnection;
     private Capture<OFFeaturesReply> newFeaturesReply;
+    
+    private TestEventLoop eventLoop;
 
     public void setUpFeaturesReply() {
        portDesc = factory.buildPortDesc()
@@ -119,13 +121,12 @@ public class OFChannelHandlerVer10Test {
         newConnectionListener = createMock(INewOFConnectionListener.class);
         newConnection = new Capture<IOFConnectionBackend>();
         newFeaturesReply = new Capture<OFFeaturesReply>();
+        eventLoop = new TestEventLoop();
 
         ctx = createMock(ChannelHandlerContext.class);
-        channelStateEvent = createMock(ChannelStateEvent.class);
         channel = createMock(Channel.class);
         timer = new HashedWheelTimer();
-        messageEvent = createMock(MessageEvent.class);
-        exceptionEventCapture = new Capture<ExceptionEvent>(CaptureType.ALL);
+        exceptionEventCapture = new Capture<Throwable>(CaptureType.ALL);
         pipeline = createMock(ChannelPipeline.class);
         writeCapture = new Capture<List<OFMessage>>(CaptureType.ALL);
         seenXids = null;
@@ -150,16 +151,14 @@ public class OFChannelHandlerVer10Test {
         replay(switchManager);
 
         // Mock ctx and channelStateEvent
-        expect(ctx.getChannel()).andReturn(channel).anyTimes();
-        expect(channelStateEvent.getChannel()).andReturn(channel).anyTimes();
-        replay(ctx, channelStateEvent);
+        expect(ctx.channel()).andReturn(channel).anyTimes();
+        expect(ctx.fireExceptionCaught(capture(exceptionEventCapture))).andReturn(ctx).anyTimes();
+        replay(ctx);
 
         /* Setup an exception event capture on the channel. Right now
          * we only expect exception events to be send up the channel.
          * However, it's easy to extend to other events if we need it
          */
-        pipeline.sendUpstream(capture(exceptionEventCapture));
-        expectLastCall().anyTimes();
         expect(pipeline.get(OFMessageDecoder.class)).andReturn(new OFMessageDecoder()).anyTimes();
         replay(pipeline);
     }
@@ -168,40 +167,34 @@ public class OFChannelHandlerVer10Test {
     public void tearDown() {
         /* ensure no exception was thrown */
         if (exceptionEventCapture.hasCaptured()) {
-            Throwable ex = exceptionEventCapture.getValue().getCause();
+            Throwable ex = exceptionEventCapture.getValue();
             ex.printStackTrace();
-            throw new AssertionError("Unexpected exception: " +
-                       ex.getClass().getName() + "(" + ex + ")");
+            Throwables.propagate(ex);
         }
         assertFalse("Unexpected messages have been captured",
                     writeCapture.hasCaptured());
         // verify all mocks.
         verify(channel);
-        verify(messageEvent);
         verify(switchManager);
         verify(ctx);
-        verify(channelStateEvent);
         verify(pipeline);
     }
 
     /** Reset the channel mock and set basic method call expectations */
     void resetChannel() {
         reset(channel);
-        expect(channel.getPipeline()).andReturn(pipeline).anyTimes();
-        expect(channel.getRemoteAddress()).andReturn(InetSocketAddress.createUnresolved("1.1.1.1", 80)).anyTimes();
-    }
-
-
-    /** reset, setup, and replay the messageEvent mock for the given
-     * messages
-     */
-    void setupMessageEvent(List<OFMessage> messages) {
-        reset(messageEvent);
-        expect(messageEvent.getMessage()).andReturn(messages).atLeastOnce();
-        replay(messageEvent);
+        expect(channel.newPromise()).andAnswer(new IAnswer<ChannelPromise>() {
+			@Override
+			public ChannelPromise answer() throws Throwable {
+				return new DefaultChannelPromise(channel);
+			}
+		}).anyTimes();
+		eventLoop = new TestEventLoop();
+		expect(channel.eventLoop()).andReturn(eventLoop).anyTimes();
+        expect(channel.pipeline()).andReturn(pipeline).anyTimes();
+        expect(channel.remoteAddress()).andReturn(InetSocketAddress.createUnresolved("1.1.1.1", 80)).anyTimes();
     }
 
-
     /** reset, setup, and replay the messageEvent mock for the given
      * messages, mock controller  send message to channel handler
      *
@@ -209,7 +202,6 @@ public class OFChannelHandlerVer10Test {
      */
     void sendMessageToHandlerWithControllerReset(List<OFMessage> messages)
             throws Exception {
-
         sendMessageToHandlerNoControllerReset(messages);
     }
 
@@ -220,9 +212,7 @@ public class OFChannelHandlerVer10Test {
      */
     void sendMessageToHandlerNoControllerReset(List<OFMessage> messages)
             throws Exception {
-        setupMessageEvent(messages);
-
-        handler.messageReceived(ctx, messageEvent);
+        handler.channelRead(ctx, messages);
     }
 
     /**
@@ -256,7 +246,7 @@ public class OFChannelHandlerVer10Test {
             Class<? extends Throwable> expectedExceptionClass) {
         assertTrue("Excpected exception not thrown",
                    exceptionEventCapture.hasCaptured());
-        Throwable caughtEx = exceptionEventCapture.getValue().getCause();
+        Throwable caughtEx = exceptionEventCapture.getValue();
         assertEquals(expectedExceptionClass, caughtEx.getClass());
         exceptionEventCapture.reset();
     }
@@ -277,16 +267,21 @@ public class OFChannelHandlerVer10Test {
             seenXids.add(xid);
         }
     }
-
+    
+    @Test
+    public void testNullMsg() throws Exception {
+    	reset(ctx);
+    	expect(ctx.fireChannelRead(null)).andReturn(ctx).once();
+    	replay(ctx, channel);
+    	
+    	// null message is not passed to the handler
+    	handler.channelRead(ctx, null);
+    	verify(channel, ctx);
+    }
 
     @Test
     public void testInitState() throws Exception {
-        // Message event needs to be list
-        expect(messageEvent.getMessage()).andReturn(null);
-        replay(channel, messageEvent);
-        handler.messageReceived(ctx, messageEvent);
-        verify(channel, messageEvent);
-        verifyExceptionCaptured(AssertionError.class);
+        replay(channel);
 
         // We don't expect to receive /any/ messages in init state since
         // channelConnected moves us to a different state
@@ -302,13 +297,10 @@ public class OFChannelHandlerVer10Test {
     public void moveToWaitHello() throws Exception {
 
         resetChannel();
-        channel.write(capture(writeCapture));
-        expectLastCall().andReturn(null).once();
+        expect(channel.writeAndFlush(capture(writeCapture))).andReturn(null).once();
         replay(channel);
-        // replay unused mocks
-        replay(messageEvent);
 
-        handler.channelConnected(ctx, channelStateEvent);
+        handler.channelActive(ctx);
 
         List<OFMessage> msgs = getMessagesFromCapture();
         assertEquals(1, msgs.size());
@@ -327,8 +319,7 @@ public class OFChannelHandlerVer10Test {
         moveToWaitHello();
 
         resetChannel();
-        channel.write(capture(writeCapture));
-        expectLastCall().andReturn(null).atLeastOnce();
+        expect(channel.writeAndFlush(capture(writeCapture))).andReturn(null).atLeastOnce();
         replay(channel);
 
         OFMessage hello = factory.buildHello().build();
@@ -387,8 +378,7 @@ public class OFChannelHandlerVer10Test {
         newConnection.getValue().setListener(connectionListener);
 
         resetChannel();
-        channel.write(capture(writeCapture));
-        expectLastCall().andReturn(null).atLeastOnce();
+        expect(channel.writeAndFlush(capture(writeCapture))).andReturn(null).atLeastOnce();
         replay(channel);
 
         // Send echo request. expect reply
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java
index 9b9b6ce897bf00c210326fed7d49e315500e11eb..643bc416ba1df288845efe8ce1debb161e96c13e 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java
@@ -23,23 +23,25 @@ import java.util.Set;
 import org.easymock.Capture;
 import org.easymock.CaptureType;
 import org.easymock.EasyMock;
+import org.easymock.IAnswer;
 import org.hamcrest.CoreMatchers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.ExceptionEvent;
-import org.jboss.netty.channel.MessageEvent;
-import org.jboss.netty.util.HashedWheelTimer;
-import org.jboss.netty.util.Timer;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.DefaultChannelPromise;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import net.floodlightcontroller.core.IOFConnectionBackend;
-import net.floodlightcontroller.core.OFConnectionCounters;
-import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandler;
-import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandshakeTimeout;
+import net.floodlightcontroller.core.internal.OFChannelInitializer.PipelineHandler;
+import net.floodlightcontroller.core.internal.OFChannelInitializer.PipelineHandshakeTimeout;
+import net.floodlightcontroller.core.test.TestEventLoop;
 import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 
@@ -68,408 +70,396 @@ import org.projectfloodlight.openflow.types.OFAuxId;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.U32;
 
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 
 
 public class OFChannelHandlerVer13Test {
-    private static final DatapathId dpid = DatapathId.of(0x42L);
-
-    private IOFSwitchManager switchManager;
-    private IOFConnectionListener connectionListener;
-    private INewOFConnectionListener newConnectionListener;
-    private IDebugCounterService debugCounterService;
-    private OFChannelHandler handler;
-    private Channel channel;
-    private Timer timer;
-    private ChannelHandlerContext ctx;
-    private MessageEvent messageEvent;
-    private ChannelStateEvent channelStateEvent;
-    private ChannelPipeline pipeline;
-    private final OFFactory factory = OFFactories.getFactory(OFVersion.OF_13);
-
-    private Capture<ExceptionEvent> exceptionEventCapture;
-    private Capture<List<OFMessage>> writeCapture;
-
-    private OFFeaturesReply featuresReply;
-    private OFPortDesc portDesc;
-
-    private Set<Long> seenXids = null;
-
-    private Capture<IOFConnectionBackend> newConnection;
-
-    private Capture<OFFeaturesReply> newFeaturesReply;
-
-    public void setUpFeaturesReply() {
-        portDesc = factory.buildPortDesc()
-                .setName("Eth1")
-                .setPortNo(OFPort.of(1))
-                .build();
-        featuresReply = factory.buildFeaturesReply()
-                .setDatapathId(dpid)
-                .setNBuffers(1)
-                .setNTables((short)1)
-                .setCapabilities(EnumSet.<OFCapabilities>of(OFCapabilities.FLOW_STATS, OFCapabilities.TABLE_STATS))
-                .setAuxiliaryId(OFAuxId.MAIN)
-                .build();
-    }
-
-    @Before
-    public void setUp() throws Exception {
-    	setUpFeaturesReply();
-        switchManager = createMock(IOFSwitchManager.class);
-        connectionListener = createMock(IOFConnectionListener.class);
-        newConnectionListener = createMock(INewOFConnectionListener.class);
-        newConnection = new Capture<IOFConnectionBackend>();
-        newFeaturesReply = new Capture<OFFeaturesReply>();
-
-        ctx = createMock(ChannelHandlerContext.class);
-        channelStateEvent = createMock(ChannelStateEvent.class);
-        channel = createMock(Channel.class);
-        timer = new HashedWheelTimer();
-        messageEvent = createMock(MessageEvent.class);
-        exceptionEventCapture = new Capture<ExceptionEvent>(CaptureType.ALL);
-        pipeline = createMock(ChannelPipeline.class);
-        writeCapture = new Capture<List<OFMessage>>(CaptureType.ALL);
-        seenXids = null;
-
-
-        // TODO: should mock IDebugCounterService and make sure
-        // the expected counters are updated.
-        debugCounterService = new DebugCounterServiceImpl();
-        debugCounterService.registerModule(OFConnectionCounters.COUNTER_MODULE);
-        SwitchManagerCounters counters =
-                new SwitchManagerCounters(debugCounterService);
-        expect(switchManager.getCounters()).andReturn(counters).anyTimes();
-        replay(switchManager);
-        handler = new OFChannelHandler(switchManager, newConnectionListener,
-                                       pipeline, debugCounterService, /* 62 is OF versions 1.0 thru 1.4 in decimal */
-                                       timer, Collections.singletonList(U32.of(62)), OFFactories.getFactory(OFVersion.OF_14));
-
-        verify(switchManager);
-        reset(switchManager);
-
-        resetChannel();
-
-        // replay controller. Reset it if you need more specific behavior
-        replay(switchManager);
-
-        // Mock ctx and channelStateEvent
-        expect(ctx.getChannel()).andReturn(channel).anyTimes();
-        expect(channelStateEvent.getChannel()).andReturn(channel).anyTimes();
-        replay(ctx, channelStateEvent);
-
-        /* Setup an exception event capture on the channel. Right now
-         * we only expect exception events to be send up the channel.
-         * However, it's easy to extend to other events if we need it
-         */
-        pipeline.sendUpstream(capture(exceptionEventCapture));
-        expectLastCall().anyTimes();
-        expect(pipeline.get(OFMessageDecoder.class)).andReturn(new OFMessageDecoder()).anyTimes();
-        replay(pipeline);
-    }
-
-    @After
-    public void tearDown() {
-        /* ensure no exception was thrown */
-        if (exceptionEventCapture.hasCaptured()) {
-            Throwable ex = exceptionEventCapture.getValue().getCause();
-            ex.printStackTrace();
-            throw new AssertionError("Unexpected exception: " +
-                       ex.getClass().getName() + "(" + ex + ")");
-        }
-        assertFalse("Unexpected messages have been captured",
-                    writeCapture.hasCaptured());
-        // verify all mocks.
-        verify(channel);
-        verify(messageEvent);
-        verify(switchManager);
-        verify(ctx);
-        verify(channelStateEvent);
-        verify(pipeline);
-    }
-
-    /** Reset the channel mock and set basic method call expectations */
-    void resetChannel() {
-        reset(channel);
-        expect(channel.getPipeline()).andReturn(pipeline).anyTimes();
-        expect(channel.getRemoteAddress()).andReturn(null).anyTimes();
-    }
-
-
-    /** reset, setup, and replay the messageEvent mock for the given
-     * messages
-     */
-    void setupMessageEvent(List<OFMessage> messages) {
-        reset(messageEvent);
-        expect(messageEvent.getMessage()).andReturn(messages).atLeastOnce();
-        replay(messageEvent);
-    }
-
-
-    /** reset, setup, and replay the messageEvent mock for the given
-     * messages, mock controller  send message to channel handler
-     *
-     * This method will reset, start replay on controller, and then verify
-     */
-    void sendMessageToHandlerWithControllerReset(List<OFMessage> messages)
-            throws Exception {
-
-        sendMessageToHandlerNoControllerReset(messages);
-    }
-
-    /** reset, setup, and replay the messageEvent mock for the given
-     * messages, mock controller  send message to channel handler
-     *
-     * This method will start replay on controller, and then verify
-     */
-    void sendMessageToHandlerNoControllerReset(List<OFMessage> messages)
-            throws Exception {
-        setupMessageEvent(messages);
-
-        handler.messageReceived(ctx, messageEvent);
-    }
-
-    /**
-     * Extract the list of OFMessages that was captured by the Channel.write()
-     * capture. Will check that something was actually captured first. We'll
-     * collapse the messages from multiple writes into a single list of
-     * OFMessages.
-     * Resets the channelWriteCapture.
-     */
-    List<OFMessage> getMessagesFromCapture() {
-        List<OFMessage> msgs = new ArrayList<OFMessage>();
-
-        assertTrue("No write on channel was captured",
-                   writeCapture.hasCaptured());
-        List<List<OFMessage>> capturedVals = writeCapture.getValues();
-
-        for (List<OFMessage> oneWriteList: capturedVals)
-            msgs.addAll(oneWriteList);
-        writeCapture.reset();
-        return msgs;
-    }
-
-
-    /**
-     * Verify that the given exception event capture (as returned by
-     * getAndInitExceptionCapture) has thrown an exception of the given
-     * expectedExceptionClass.
-     * Resets the capture
-     */
-    void verifyExceptionCaptured(
-            Class<? extends Throwable> expectedExceptionClass) {
-        assertTrue("Excpected exception not thrown",
-                   exceptionEventCapture.hasCaptured());
-        Throwable caughtEx = exceptionEventCapture.getValue().getCause();
-        assertEquals(expectedExceptionClass, caughtEx.getClass());
-        exceptionEventCapture.reset();
-    }
-
-    /** make sure that the transaction ids in the given messages are
-     * not 0 and differ between each other.
-     * While it's not a defect per se if the xids are we want to ensure
-     * we use different ones for each message we send.
-     */
-    void verifyUniqueXids(List<OFMessage> msgs) {
-        if (seenXids == null)
-            seenXids = new HashSet<Long>();
-        for (OFMessage m: msgs)  {
-            long xid = m.getXid();
-            assertTrue("Xid in messags is 0", xid != 0);
-            assertFalse("Xid " + xid + " has already been used",
-                        seenXids.contains(xid));
-            seenXids.add(xid);
-        }
-    }
-
-
-    @Test
-    public void testInitState() throws Exception {
-        // Message event needs to be list
-        expect(messageEvent.getMessage()).andReturn(null);
-        replay(channel, messageEvent);
-        handler.messageReceived(ctx, messageEvent);
-        verify(channel, messageEvent);
-        verifyExceptionCaptured(AssertionError.class);
-
-        // We don't expect to receive /any/ messages in init state since
-        // channelConnected moves us to a different state
-        OFMessage m = factory.buildHello().build();
-        sendMessageToHandlerWithControllerReset(ImmutableList.<OFMessage>of(m));
-
-        verifyExceptionCaptured(SwitchStateException.class);
-        assertThat(handler.getStateForTesting(), CoreMatchers.instanceOf(OFChannelHandler.InitState.class));
-    }
-
-    /* Move the channel from scratch to WAIT_HELLO state */
-    @Test
-    public void moveToWaitHello() throws Exception {
-        resetChannel();
-        channel.write(capture(writeCapture));
-        expectLastCall().andReturn(null).once();
-        replay(channel);
-        // replay unused mocks
-        replay(messageEvent);
-
-        handler.channelConnected(ctx, channelStateEvent);
-
-        List<OFMessage> msgs = getMessagesFromCapture();
-        assertEquals(1, msgs.size());
-        assertEquals(OFType.HELLO, msgs.get(0).getType());
-        assertThat(handler.getStateForTesting(), CoreMatchers.instanceOf(OFChannelHandler.WaitHelloState.class));
-        verifyUniqueXids(msgs);
-    }
-
-    /** Move the channel from scratch to WAIT_FEATURES_REPLY state
-     * Builds on moveToWaitHello()
-     * adds testing for WAIT_HELLO state
-     */
-    @Test
-    public void moveToWaitFeaturesReply() throws Exception {
-        moveToWaitHello();
-        resetChannel();
-        channel.write(capture(writeCapture));
-        expectLastCall().andReturn(null).atLeastOnce();
-        replay(channel);
-
-        OFMessage hello = factory.buildHello().build();
-        sendMessageToHandlerWithControllerReset(ImmutableList.<OFMessage>of(hello));
-
-        List<OFMessage> msgs = getMessagesFromCapture();
-        assertEquals(1, msgs.size());
-        assertEquals(OFType.FEATURES_REQUEST, msgs.get(0).getType());
-        verifyUniqueXids(msgs);
-
-        assertThat(handler.getStateForTesting(), CoreMatchers.instanceOf(OFChannelHandler.WaitFeaturesReplyState.class));
-    }
-
-
-    /** Move the channel from scratch to WAIT_FEATURES_REPLY state
-     * Builds on moveToWaitHello()
-     * adds testing for WAIT_HELLO state
-     */
-    @Test
-    public void moveToComplete() throws Exception {
-        moveToWaitFeaturesReply();
-
-        reset(pipeline);
-        HandshakeTimeoutHandler newHandler = new HandshakeTimeoutHandler(
-                                                                         handler,
-                                                                         timer,
-                                                                         PipelineHandshakeTimeout.SWITCH);
-
-        expect(
-               pipeline.replace(EasyMock.eq(PipelineHandler.CHANNEL_HANDSHAKE_TIMEOUT),
-                                EasyMock.eq(PipelineHandler.SWITCH_HANDSHAKE_TIMEOUT),
-                                EasyMock.anyObject(HandshakeTimeoutHandler.class))).andReturn(newHandler)
-                                                                          .once();
-
-        replay(pipeline);
-
-        newConnectionListener.connectionOpened(capture(newConnection), capture(newFeaturesReply));
-        expectLastCall().once();
-        replay(newConnectionListener);
-
-        sendMessageToHandlerWithControllerReset(Collections.<OFMessage>singletonList(featuresReply));
-
-        assertThat(handler.getStateForTesting(), CoreMatchers.instanceOf(OFChannelHandler.CompleteState.class));
-        assertTrue("A connection has been created and set", handler.getConnectionForTesting() != null);
-        verify(newConnectionListener);
-        assertTrue(newConnection.hasCaptured());
-        assertThat(newFeaturesReply.getValue(), equalTo(featuresReply));
-    }
-
-    /**
-     * Test dispatch of messages while in Complete state
-     */
-    @Test
-    public void testMessageDispatchComplete() throws Exception {
-        moveToComplete();
-        newConnection.getValue().setListener(connectionListener);
-
-        resetChannel();
-        channel.write(capture(writeCapture));
-        expectLastCall().andReturn(null).atLeastOnce();
-        replay(channel);
+	private static final DatapathId dpid = DatapathId.of(0x42L);
+
+	private IOFSwitchManager switchManager;
+	private IOFConnectionListener connectionListener;
+	private INewOFConnectionListener newConnectionListener;
+	private IDebugCounterService debugCounterService;
+	private OFChannelHandler handler;
+	private Channel channel;
+	private Timer timer;
+	private ChannelHandlerContext ctx;
+	private ChannelPipeline pipeline;
+	private final OFFactory factory = OFFactories.getFactory(OFVersion.OF_13);
+
+	private Capture<Throwable> exceptionEventCapture;
+	private Capture<List<OFMessage>> writeCapture;
+
+	private OFFeaturesReply featuresReply;
+	private OFPortDesc portDesc;
+
+	private Set<Long> seenXids = null;
+
+	private Capture<IOFConnectionBackend> newConnection;
+
+	private Capture<OFFeaturesReply> newFeaturesReply;
+
+	private TestEventLoop eventLoop;
+
+	public void setUpFeaturesReply() {
+		portDesc = factory.buildPortDesc()
+				.setName("Eth1")
+				.setPortNo(OFPort.of(1))
+				.build();
+		featuresReply = factory.buildFeaturesReply()
+				.setDatapathId(dpid)
+				.setNBuffers(1)
+				.setNTables((short)1)
+				.setCapabilities(EnumSet.<OFCapabilities>of(OFCapabilities.FLOW_STATS, OFCapabilities.TABLE_STATS))
+				.setAuxiliaryId(OFAuxId.MAIN)
+				.build();
+	}
+
+	@Before
+	public void setUp() throws Exception {
+		setUpFeaturesReply();
+		switchManager = createMock(IOFSwitchManager.class);
+		connectionListener = createMock(IOFConnectionListener.class);
+		newConnectionListener = createMock(INewOFConnectionListener.class);
+		newConnection = new Capture<IOFConnectionBackend>();
+		newFeaturesReply = new Capture<OFFeaturesReply>();
+        eventLoop = new TestEventLoop();
+
+		ctx = createMock(ChannelHandlerContext.class);
+		channel = createMock(Channel.class);
+		timer = new HashedWheelTimer();
+		exceptionEventCapture = new Capture<Throwable>(CaptureType.ALL);
+		pipeline = createMock(ChannelPipeline.class);
+		writeCapture = new Capture<List<OFMessage>>(CaptureType.ALL);
+		seenXids = null;
+
+
+		// TODO: should mock IDebugCounterService and make sure
+		// the expected counters are updated.
+		debugCounterService = new DebugCounterServiceImpl();
+		debugCounterService.registerModule(OFConnectionCounters.COUNTER_MODULE);
+		SwitchManagerCounters counters =
+				new SwitchManagerCounters(debugCounterService);
+		expect(switchManager.getCounters()).andReturn(counters).anyTimes();
+		replay(switchManager);
+		handler = new OFChannelHandler(switchManager, newConnectionListener,
+				pipeline, debugCounterService, /* 62 is OF versions 1.0 thru 1.4 in decimal */
+				timer, Collections.singletonList(U32.of(62)), OFFactories.getFactory(OFVersion.OF_14));
+
+		verify(switchManager);
+		reset(switchManager);
+
+		resetChannel();
+
+		// replay controller. Reset it if you need more specific behavior
+		replay(switchManager);
+
+		// Mock ctx and channelStateEvent
+		expect(ctx.channel()).andReturn(channel).anyTimes();
+		expect(ctx.fireExceptionCaught(capture(exceptionEventCapture))).andReturn(ctx).anyTimes();
+		replay(ctx);
+
+		/* Setup an exception event capture on the channel. Right now
+		 * we only expect exception events to be send up the channel.
+		 * However, it's easy to extend to other events if we need it
+		 */
+		expect(pipeline.get(OFMessageDecoder.class)).andReturn(new OFMessageDecoder()).anyTimes();
+		replay(pipeline);
+	}
+
+	@After
+	public void tearDown() {
+		/* ensure no exception was thrown */
+		if (exceptionEventCapture.hasCaptured()) {
+			Throwable ex = exceptionEventCapture.getValue();
+			ex.printStackTrace();
+			Throwables.propagate(ex);
+		}
+		assertFalse("Unexpected messages have been captured",
+				writeCapture.hasCaptured());
+		// verify all mocks.
+		verify(channel);
+		verify(switchManager);
+		verify(ctx);
+		verify(pipeline);
+	}
+
+	/** Reset the channel mock and set basic method call expectations */
+	void resetChannel() {
+		reset(channel);
+		expect(channel.newPromise()).andAnswer(new IAnswer<ChannelPromise>() {
+			@Override
+			public ChannelPromise answer() throws Throwable {
+				return new DefaultChannelPromise(channel);
+			}
+		}).anyTimes();
+		eventLoop = new TestEventLoop();
+		expect(channel.eventLoop()).andReturn(eventLoop).anyTimes();
+		expect(channel.pipeline()).andReturn(pipeline).anyTimes();
+		expect(channel.remoteAddress()).andReturn(null).anyTimes();
+	}
+
+	/** reset, setup, and replay the messageEvent mock for the given
+	 * messages, mock controller  send message to channel handler
+	 *
+	 * This method will reset, start replay on controller, and then verify
+	 */
+	void sendMessageToHandlerWithControllerReset(List<OFMessage> messages)
+			throws Exception {
+		sendMessageToHandlerNoControllerReset(messages);
+	}
+
+	/** reset, setup, and replay the messageEvent mock for the given
+	 * messages, mock controller  send message to channel handler
+	 *
+	 * This method will start replay on controller, and then verify
+	 */
+	void sendMessageToHandlerNoControllerReset(List<OFMessage> messages)
+			throws Exception {
+		handler.channelRead(ctx, messages);
+	}
+
+	/**
+	 * Extract the list of OFMessages that was captured by the Channel.write()
+	 * capture. Will check that something was actually captured first. We'll
+	 * collapse the messages from multiple writes into a single list of
+	 * OFMessages.
+	 * Resets the channelWriteCapture.
+	 */
+	List<OFMessage> getMessagesFromCapture() {
+		List<OFMessage> msgs = new ArrayList<OFMessage>();
+
+		assertTrue("No write on channel was captured",
+				writeCapture.hasCaptured());
+		List<List<OFMessage>> capturedVals = writeCapture.getValues();
+
+		for (List<OFMessage> oneWriteList: capturedVals)
+			msgs.addAll(oneWriteList);
+		writeCapture.reset();
+		return msgs;
+	}
+
+
+	/**
+	 * Verify that the given exception event capture (as returned by
+	 * getAndInitExceptionCapture) has thrown an exception of the given
+	 * expectedExceptionClass.
+	 * Resets the capture
+	 */
+	void verifyExceptionCaptured(Class<? extends Throwable> expectedExceptionClass) {
+		assertTrue("Excpected exception not thrown", exceptionEventCapture.hasCaptured());
+		Throwable caughtEx = exceptionEventCapture.getValue();
+		assertEquals(expectedExceptionClass, caughtEx.getClass());
+		exceptionEventCapture.reset();
+	}
+
+	/** make sure that the transaction ids in the given messages are
+	 * not 0 and differ between each other.
+	 * While it's not a defect per se if the xids are we want to ensure
+	 * we use different ones for each message we send.
+	 */
+	void verifyUniqueXids(List<OFMessage> msgs) {
+		if (seenXids == null)
+			seenXids = new HashSet<Long>();
+		for (OFMessage m: msgs)  {
+			long xid = m.getXid();
+			assertTrue("Xid in messags is 0", xid != 0);
+			assertFalse("Xid " + xid + " has already been used",
+					seenXids.contains(xid));
+			seenXids.add(xid);
+		}
+	}
+
+	@Test
+	public void testNullMsg() throws Exception {
+		reset(ctx);
+		expect(ctx.fireChannelRead(null)).andReturn(ctx).once();
+		replay(ctx, channel);
+
+		// null message is not passed to the handler
+		handler.channelRead(ctx, null);
+		verify(channel, ctx);
+	}
+
+	@Test
+	public void testInitState() throws Exception {
+		replay(channel);
+
+		// We don't expect to receive /any/ messages in init state since
+		// channelConnected moves us to a different state
+		OFMessage m = factory.buildHello().build();
+		sendMessageToHandlerWithControllerReset(ImmutableList.<OFMessage>of(m));
+
+		verifyExceptionCaptured(SwitchStateException.class);
+		assertThat(handler.getStateForTesting(), CoreMatchers.instanceOf(OFChannelHandler.InitState.class));
+	}
+
+	/* Move the channel from scratch to WAIT_HELLO state */
+	@Test
+	public void moveToWaitHello() throws Exception {
+		resetChannel();
+		expect(channel.writeAndFlush(capture(writeCapture))).andReturn(null).once();
+		replay(channel);
+
+		handler.channelActive(ctx);
+		eventLoop.runTasks();
+
+		List<OFMessage> msgs = getMessagesFromCapture();
+		assertEquals(1, msgs.size());
+		assertEquals(OFType.HELLO, msgs.get(0).getType());
+		assertThat(handler.getStateForTesting(), CoreMatchers.instanceOf(OFChannelHandler.WaitHelloState.class));
+		verifyUniqueXids(msgs);
+	}
+
+	/** Move the channel from scratch to WAIT_FEATURES_REPLY state
+	 * Builds on moveToWaitHello()
+	 * adds testing for WAIT_HELLO state
+	 */
+	@Test
+	public void moveToWaitFeaturesReply() throws Exception {
+		moveToWaitHello();
+		resetChannel();
+		expect(channel.writeAndFlush(capture(writeCapture))).andReturn(null).once();
+		replay(channel);
+
+		OFMessage hello = factory.buildHello().build();
+		sendMessageToHandlerWithControllerReset(ImmutableList.<OFMessage>of(hello));
+
+		List<OFMessage> msgs = getMessagesFromCapture();
+		assertEquals(1, msgs.size());
+		assertEquals(OFType.FEATURES_REQUEST, msgs.get(0).getType());
+		verifyUniqueXids(msgs);
+
+		assertThat(handler.getStateForTesting(), CoreMatchers.instanceOf(OFChannelHandler.WaitFeaturesReplyState.class));
+	}
+
+
+	/** Move the channel from scratch to WAIT_FEATURES_REPLY state
+	 * Builds on moveToWaitHello()
+	 * adds testing for WAIT_HELLO state
+	 */
+	@Test
+	public void moveToComplete() throws Exception {
+		moveToWaitFeaturesReply();
+
+		reset(pipeline);
+		HandshakeTimeoutHandler newHandler = new HandshakeTimeoutHandler(
+				handler,
+				timer,
+				PipelineHandshakeTimeout.SWITCH);
+
+		expect(
+				pipeline.replace(EasyMock.eq(PipelineHandler.CHANNEL_HANDSHAKE_TIMEOUT),
+						EasyMock.eq(PipelineHandler.SWITCH_HANDSHAKE_TIMEOUT),
+						EasyMock.anyObject(HandshakeTimeoutHandler.class))).andReturn(newHandler)
+						.once();
+
+		replay(pipeline);
+
+		newConnectionListener.connectionOpened(capture(newConnection), capture(newFeaturesReply));
+		expectLastCall().once();
+		replay(newConnectionListener);
+
+		sendMessageToHandlerWithControllerReset(Collections.<OFMessage>singletonList(featuresReply));
+
+		assertThat(handler.getStateForTesting(), CoreMatchers.instanceOf(OFChannelHandler.CompleteState.class));
+		assertTrue("A connection has been created and set", handler.getConnectionForTesting() != null);
+		verify(newConnectionListener);
+		assertTrue(newConnection.hasCaptured());
+		assertThat(newFeaturesReply.getValue(), equalTo(featuresReply));
+	}
+
+	/**
+	 * Test dispatch of messages while in Complete state
+	 */
+	@Test
+	public void testMessageDispatchComplete() throws Exception {
+		moveToComplete();
+		newConnection.getValue().setListener(connectionListener);
+
+		resetChannel();
+		expect(channel.writeAndFlush(capture(writeCapture))).andReturn(null).once();
+		replay(channel);
 
-        // Send echo request. expect reply
-        OFMessage echoRequest = factory.buildEchoRequest().build();
-        sendMessageToHandlerWithControllerReset(ImmutableList.<OFMessage>of(echoRequest));
+		// Send echo request. expect reply
+		OFMessage echoRequest = factory.buildEchoRequest().build();
+		sendMessageToHandlerWithControllerReset(ImmutableList.<OFMessage>of(echoRequest));
 
-        List<OFMessage> msgs = getMessagesFromCapture();
-        assertEquals(1, msgs.size());
-        assertEquals(OFType.ECHO_REPLY, msgs.get(0).getType());
+		List<OFMessage> msgs = getMessagesFromCapture();
+		assertEquals(1, msgs.size());
+		assertEquals(OFType.ECHO_REPLY, msgs.get(0).getType());
 
 
-        // Send barrier reply. expect dispatch
-        OFBarrierReply barrierReply = factory.buildBarrierReply()
-                .build();
+		// Send barrier reply. expect dispatch
+		OFBarrierReply barrierReply = factory.buildBarrierReply()
+				.build();
 
-        resetAndExpectConnectionListener(barrierReply);
+		resetAndExpectConnectionListener(barrierReply);
 
 
-        // Send packet in. expect dispatch
-        OFFlowRemoved flowRemoved = factory.buildFlowRemoved()
-                .build();
+		// Send packet in. expect dispatch
+		OFFlowRemoved flowRemoved = factory.buildFlowRemoved()
+				.build();
 
-        resetAndExpectConnectionListener(flowRemoved);
+		resetAndExpectConnectionListener(flowRemoved);
 
-        // Send get config reply. expect dispatch
-        OFGetConfigReply getConfigReply = factory.buildGetConfigReply()
-                .build();
+		// Send get config reply. expect dispatch
+		OFGetConfigReply getConfigReply = factory.buildGetConfigReply()
+				.build();
 
-        resetAndExpectConnectionListener(getConfigReply);
+		resetAndExpectConnectionListener(getConfigReply);
 
-        // Send packet in. expect dispatch
-        OFPacketIn pi = factory.buildPacketIn()
-                .setReason(OFPacketInReason.NO_MATCH)
-                .build();
+		// Send packet in. expect dispatch
+		OFPacketIn pi = factory.buildPacketIn()
+				.setReason(OFPacketInReason.NO_MATCH)
+				.build();
 
-        resetAndExpectConnectionListener(pi);
+		resetAndExpectConnectionListener(pi);
 
-        // Send port status. expect dispatch
-        OFPortStatus portStatus = factory.buildPortStatus()
-                .setReason(OFPortReason.DELETE)
-                .setDesc(portDesc)
-                .build();
+		// Send port status. expect dispatch
+		OFPortStatus portStatus = factory.buildPortStatus()
+				.setReason(OFPortReason.DELETE)
+				.setDesc(portDesc)
+				.build();
 
-        resetAndExpectConnectionListener(portStatus);
+		resetAndExpectConnectionListener(portStatus);
 
-        // Send queue reply. expect dispatch
-        OFQueueGetConfigReply queueReply = factory.buildQueueGetConfigReply()
-                .build();
+		// Send queue reply. expect dispatch
+		OFQueueGetConfigReply queueReply = factory.buildQueueGetConfigReply()
+				.build();
 
-        resetAndExpectConnectionListener(queueReply);
+		resetAndExpectConnectionListener(queueReply);
 
-        // Send stat reply. expect dispatch
-        OFFlowStatsReply statReply = factory.buildFlowStatsReply()
-                .build();
+		// Send stat reply. expect dispatch
+		OFFlowStatsReply statReply = factory.buildFlowStatsReply()
+				.build();
 
-        resetAndExpectConnectionListener(statReply);
+		resetAndExpectConnectionListener(statReply);
 
-        // Send role reply. expect dispatch
-        OFRoleReply roleReply = factory.buildRoleReply()
-                .setRole(OFControllerRole.ROLE_MASTER)
-                .build();
+		// Send role reply. expect dispatch
+		OFRoleReply roleReply = factory.buildRoleReply()
+				.setRole(OFControllerRole.ROLE_MASTER)
+				.build();
 
-        resetAndExpectConnectionListener(roleReply);
+		resetAndExpectConnectionListener(roleReply);
 
-        // Send experimenter. expect dispatch
-        OFBsnSetAuxCxnsReply auxReply = factory.buildBsnSetAuxCxnsReply()
-                .build();
+		// Send experimenter. expect dispatch
+		OFBsnSetAuxCxnsReply auxReply = factory.buildBsnSetAuxCxnsReply()
+				.build();
 
-        resetAndExpectConnectionListener(auxReply);
+		resetAndExpectConnectionListener(auxReply);
 
-    }
+	}
 
-    public void resetAndExpectConnectionListener(OFMessage m) throws Exception{
-        reset(connectionListener);
-        connectionListener.messageReceived(handler.getConnectionForTesting(), m);
-        expectLastCall().once();
-        replay(connectionListener);
+	public void resetAndExpectConnectionListener(OFMessage m) throws Exception{
+		reset(connectionListener);
+		connectionListener.messageReceived(handler.getConnectionForTesting(), m);
+		expectLastCall().once();
+		replay(connectionListener);
 
-        sendMessageToHandlerWithControllerReset(Collections.<OFMessage>singletonList(m));
+		sendMessageToHandlerWithControllerReset(Collections.<OFMessage>singletonList(m));
 
-        verify(connectionListener);
-    }
+		verify(connectionListener);
+	}
 }
diff --git a/src/test/java/net/floodlightcontroller/core/OFConnectionTest.java b/src/test/java/net/floodlightcontroller/core/internal/OFConnectionTest.java
similarity index 89%
rename from src/test/java/net/floodlightcontroller/core/OFConnectionTest.java
rename to src/test/java/net/floodlightcontroller/core/internal/OFConnectionTest.java
index d65788b36a3cff928e09301fdeb1362f3f78121d..23230074aacb2c69d45801bda2c5bdaa4bddff1c 100644
--- a/src/test/java/net/floodlightcontroller/core/OFConnectionTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFConnectionTest.java
@@ -1,4 +1,4 @@
-package net.floodlightcontroller.core;
+package net.floodlightcontroller.core.internal;
 
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.expect;
@@ -13,12 +13,20 @@ import org.easymock.Capture;
 import org.easymock.EasyMock;
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.Matchers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.util.HashedWheelTimer;
-import org.jboss.netty.util.Timer;
+
+import io.netty.channel.Channel;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import net.floodlightcontroller.core.SwitchDisconnectedException;
+import net.floodlightcontroller.core.internal.OFConnection;
+import net.floodlightcontroller.core.internal.OFConnectionCounters;
+import net.floodlightcontroller.core.internal.OFErrorMsgException;
+import net.floodlightcontroller.core.test.TestEventLoop;
 import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
 
@@ -62,17 +70,28 @@ public class OFConnectionTest {
     private OFConnection conn;
     private DatapathId switchId;
     private Timer timer;
+    private TestEventLoop eventLoop;
 
     @Before
     public void setUp() throws Exception {
         factory = OFFactories.getFactory(OFVersion.OF_13);
         switchId = DatapathId.of(1);
         timer = new HashedWheelTimer();
-        channel = EasyMock.createNiceMock(Channel.class);        
+        channel = EasyMock.createMock(Channel.class);        
         IDebugCounterService debugCounterService = new DebugCounterServiceImpl();
         debugCounterService.registerModule(OFConnectionCounters.COUNTER_MODULE);
         conn = new OFConnection(switchId, factory, channel, OFAuxId.MAIN,
                                 debugCounterService, timer);
+        eventLoop = new TestEventLoop();
+        
+        expect(channel.eventLoop()).andReturn(eventLoop).anyTimes();
+    }
+    
+    @After
+    public void tearDown() throws Exception {
+    	if (timer != null) {
+    		timer.stop();
+    	}
     }
 
     @Test(timeout = 5000)
@@ -84,6 +103,7 @@ public class OFConnectionTest {
         assertThat("Connection should have 1 pending request",
                 conn.getPendingRequestIds().size(), equalTo(1));
 
+        eventLoop.runTasks();
         assertThat("Should have captured MsgList", cMsgList.getValue(),
                 Matchers.<OFMessage> contains(echoRequest));
 
@@ -111,7 +131,8 @@ public class OFConnectionTest {
         ListenableFuture<List<OFFlowStatsReply>> future = conn.writeStatsRequest(flowStatsRequest);
         assertThat("Connection should have 1 pending request",
                 conn.getPendingRequestIds().size(), equalTo(1));
-
+        
+        eventLoop.runTasks();
         assertThat("Should have captured MsgList", cMsgList.getValue(),
                 Matchers.<OFMessage> contains(flowStatsRequest));
 
@@ -142,9 +163,9 @@ public class OFConnectionTest {
     }
 
     private Capture<List<OFMessage>> prepareChannelForWriteList() {
-        EasyMock.expect(channel.isConnected()).andReturn(Boolean.TRUE).anyTimes();
+        EasyMock.expect(channel.isActive()).andReturn(Boolean.TRUE).anyTimes();
         Capture<List<OFMessage>> cMsgList = new Capture<>();
-        expect(channel.write(capture(cMsgList))).andReturn(null).once();
+        expect(channel.writeAndFlush(capture(cMsgList))).andReturn(null).once();
         replay(channel);
         return cMsgList;
     }
@@ -159,6 +180,7 @@ public class OFConnectionTest {
         assertThat("Connection should have 1 pending request",
                 conn.getPendingRequestIds().size(), equalTo(1));
 
+        eventLoop.runTasks();
         assertThat("Should have captured MsgList", cMsgList.getValue(),
                 Matchers.<OFMessage> contains(roleRequest));
 
@@ -181,7 +203,7 @@ public class OFConnectionTest {
     @Test(timeout = 5000)
     public void testWriteRequestNotConnectedFailure() throws InterruptedException,
             ExecutionException {
-        EasyMock.expect(channel.isConnected()).andReturn(Boolean.FALSE).anyTimes();
+        EasyMock.expect(channel.isActive()).andReturn(Boolean.FALSE).anyTimes();
         replay(channel);
 
         OFEchoRequest echoRequest = factory.echoRequest(new byte[] {});
@@ -216,7 +238,7 @@ public class OFConnectionTest {
                 conn.getPendingRequestIds().isEmpty(), equalTo(true));
     }
 
-    /** write a packetOut, which is buffered */
+    /** write a packetOut, which is not buffered */
     @Test(timeout = 5000)
     public void testSingleMessageWrite() throws InterruptedException, ExecutionException {
         Capture<List<OFMessage>> cMsgList = prepareChannelForWriteList();
@@ -227,6 +249,7 @@ public class OFConnectionTest {
                 .build();
         
         conn.write(packetOut);
+        eventLoop.runTasks();
         assertThat("Write should have been flushed", cMsgList.hasCaptured(), equalTo(true));
         
         List<OFMessage> value = cMsgList.getValue();
@@ -247,7 +270,7 @@ public class OFConnectionTest {
                 .build();
 
         conn.write(ImmutableList.of(hello, packetOut));
-
+        eventLoop.runTasks();
         assertThat("Write should have been written", cMsgList.hasCaptured(), equalTo(true));
         List<OFMessage> value = cMsgList.getValue();
         logger.info("Captured channel write: "+value);
diff --git a/src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchBaseTest.java
similarity index 95%
rename from src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java
rename to src/test/java/net/floodlightcontroller/core/internal/OFSwitchBaseTest.java
index 24db287af6da5c28214280f1a240991dafc27e55..5c067f060310082d3f64a9985f892111f0eb5f5e 100644
--- a/src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchBaseTest.java
@@ -14,7 +14,7 @@
  *    under the License.
  **/
 
-package net.floodlightcontroller.core;
+package net.floodlightcontroller.core.internal;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
@@ -41,7 +41,17 @@ import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 
+import net.floodlightcontroller.core.IOFConnectionBackend;
+import net.floodlightcontroller.core.IOFSwitchBackend;
+import net.floodlightcontroller.core.LogicalOFMessageCategory;
+import net.floodlightcontroller.core.PortChangeEvent;
+import net.floodlightcontroller.core.PortChangeType;
+import net.floodlightcontroller.core.SwitchDescription;
+import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted;
+import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted;
+import net.floodlightcontroller.core.SwitchDriverSubHandshakeNotStarted;
 import net.floodlightcontroller.core.internal.IOFSwitchManager;
+import net.floodlightcontroller.core.internal.OFSwitch;
 import net.floodlightcontroller.core.internal.SwitchManagerCounters;
 import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
@@ -49,6 +59,8 @@ import net.floodlightcontroller.debugcounter.IDebugCounterService;
 import org.projectfloodlight.openflow.protocol.OFControllerRole;
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFFlowAdd;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortConfig;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
@@ -112,7 +124,7 @@ public class OFSwitchBaseTest {
             new PortChangeEvent(portBar1, PortChangeType.DELETE);
     private final PortChangeEvent portBar2Del =
             new PortChangeEvent(portBar2, PortChangeType.DELETE);
-    private Capture<OFMessage> capturedMessage;
+    private Capture<Iterable<OFMessage>> capturedMessage;
     private OFFactory factory;
 
     @Before
@@ -134,9 +146,8 @@ public class OFSwitchBaseTest {
                 .build();
 
         IOFConnectionBackend conn = EasyMock.createNiceMock(IOFConnectionBackend.class);
-        capturedMessage = new Capture<OFMessage>();
-        conn.write(EasyMock.capture(capturedMessage));
-        expectLastCall().anyTimes();
+        capturedMessage = new Capture<Iterable<OFMessage>>();
+        expect(conn.write(EasyMock.capture(capturedMessage))).andReturn(Collections.<OFMessage>emptyList()).atLeastOnce();
         expect(conn.getOFFactory()).andReturn(factory).anyTimes();
         expect(conn.getAuxId()).andReturn(OFAuxId.MAIN).anyTimes();
         EasyMock.replay(conn);
@@ -144,6 +155,7 @@ public class OFSwitchBaseTest {
         IOFConnectionBackend auxConn = EasyMock.createNiceMock(IOFConnectionBackend.class);
         expect(auxConn.getOFFactory()).andReturn(factory).anyTimes();
         expect(auxConn.getAuxId()).andReturn(OFAuxId.of(1)).anyTimes();
+        expect(auxConn.write(EasyMock.capture(capturedMessage))).andReturn(Collections.<OFMessage>emptyList()).once();
         EasyMock.replay(auxConn);
 
         sw = new OFSwitchTest(conn, switchManager);
@@ -1395,12 +1407,42 @@ public class OFSwitchBaseTest {
         reset(switchManager);
         expect(switchManager.isCategoryRegistered(category)).andReturn(true);
         switchManager.handleOutgoingMessage(sw, testMessage);
-        expectLastCall();
+        expectLastCall().once();
         replay(switchManager);
-
+        
         sw.write(testMessage, category);
 
         verify(switchManager);
     }
 
-}
+
+	@Test
+	public void testMasterSlaveWrites() {
+		OFFactory factory = OFFactories.getFactory(OFVersion.OF_13);
+		OFFlowAdd fa = factory.buildFlowAdd().build();
+		OFFlowStatsRequest fsr = factory.buildFlowStatsRequest().build();
+		List<OFMessage> msgList = new ArrayList<OFMessage>();
+		msgList.add(fa);
+		msgList.add(fsr);
+		
+		reset(switchManager);
+        expect(switchManager.isCategoryRegistered(LogicalOFMessageCategory.MAIN)).andReturn(true).times(6);
+        switchManager.handleOutgoingMessage(sw, fa);
+        expectLastCall().times(2);
+        switchManager.handleOutgoingMessage(sw, fsr);
+        expectLastCall().times(4);
+        replay(switchManager);
+
+		/* test master -- both messages should be written */
+		sw.setControllerRole(OFControllerRole.ROLE_MASTER);
+		assertTrue(sw.write(fa));
+		assertTrue(sw.write(fsr));
+		assertEquals(Collections.<OFMessage>emptyList(), sw.write(msgList));
+		
+		/* test slave -- flow-add (mod op) should fail each time; flow stats (read op) should pass */
+		sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
+		assertFalse(sw.write(fa)); /* flow-add should be stopped (mod op) */
+		assertTrue(sw.write(fsr)); /* stats request makes it (read op) */
+		assertEquals(Collections.<OFMessage>singletonList(fa), sw.write(msgList)); /* return bad flow-add */
+	}
+}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java
index 675873a5eeac46c7e8db6cef30e31ea02680fd08..9f254d658bbe130ac27394707bd8fd619e825218 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java
@@ -27,9 +27,11 @@ import java.util.concurrent.TimeUnit;
 import org.easymock.EasyMock;
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.Matchers;
-import org.jboss.netty.util.Timeout;
-import org.jboss.netty.util.Timer;
-import org.jboss.netty.util.TimerTask;
+
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -521,10 +523,8 @@ public abstract class OFSwitchHandlerTestBase {
 		// TODO: hmmm. While it's not incorrect that we set the attribute
 		// again it looks odd. Maybe change
 		expect(sw.getOFFactory()).andReturn(factory).anyTimes();
-		sw.write(anyObject(OFMessage.class));
-		expectLastCall().anyTimes();
-		sw.write(anyObject(Iterable.class));
-		expectLastCall().anyTimes();
+		expect(sw.write(anyObject(OFMessage.class))).andReturn(true).anyTimes();
+		expect(sw.write(anyObject(Iterable.class))).andReturn(Collections.EMPTY_LIST).anyTimes();
 		expect(sw.getNumTables()).andStubReturn((short)0);
 		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, supportsNxRole);
 		expectLastCall().anyTimes();
@@ -599,10 +599,8 @@ public abstract class OFSwitchHandlerTestBase {
 		// prepare mocks and inject the role reply message
 		reset(sw);
 		expect(sw.getOFFactory()).andReturn(factory).anyTimes();
-		sw.write(anyObject(OFMessage.class));
-		expectLastCall().anyTimes();
-		sw.write(anyObject(Iterable.class));
-		expectLastCall().anyTimes();
+		expect(sw.write(anyObject(OFMessage.class))).andReturn(true).anyTimes();
+		expect(sw.write(anyObject(Iterable.class))).andReturn(Collections.EMPTY_LIST).anyTimes();
 		expect(sw.getTables()).andStubReturn(Collections.EMPTY_LIST);
 		expect(sw.getNumTables()).andStubReturn((short) 0);
 		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true);
@@ -695,10 +693,8 @@ public abstract class OFSwitchHandlerTestBase {
 		// prepare mocks and inject the role reply message
 		reset(sw);
 		expect(sw.getOFFactory()).andReturn(factory).anyTimes();
-		sw.write(anyObject(OFMessage.class));
-		expectLastCall().anyTimes();
-		sw.write(anyObject(Iterable.class));
-		expectLastCall().anyTimes();
+		expect(sw.write(anyObject(OFMessage.class))).andReturn(true).anyTimes();
+		expect(sw.write(anyObject(Iterable.class))).andReturn(Collections.EMPTY_LIST).anyTimes();
 		expect(sw.getNumTables()).andStubReturn((short)0);
 		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false);
 		expectLastCall().once();
@@ -758,10 +754,8 @@ public abstract class OFSwitchHandlerTestBase {
 		// prepare mocks and inject the role reply message
 		reset(sw);
 		expect(sw.getOFFactory()).andReturn(factory).anyTimes();
-		sw.write(anyObject(OFMessage.class));
-		expectLastCall().anyTimes();
-		sw.write(anyObject(Iterable.class));
-		expectLastCall().anyTimes();
+		expect(sw.write(anyObject(OFMessage.class))).andReturn(true).anyTimes();
+		expect(sw.write(anyObject(Iterable.class))).andReturn(Collections.EMPTY_LIST).anyTimes();
 		expect(sw.getNumTables()).andStubReturn((short)0);
 		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false);
 		expectLastCall().once();
@@ -922,10 +916,8 @@ public abstract class OFSwitchHandlerTestBase {
 		// prepare mocks and inject the role reply message
 		reset(sw);
 		expect(sw.getOFFactory()).andReturn(factory).anyTimes();
-		sw.write(anyObject(OFMessage.class));
-		expectLastCall().anyTimes();
-		sw.write(anyObject(Iterable.class));
-		expectLastCall().anyTimes();
+		expect(sw.write(anyObject(OFMessage.class))).andReturn(true).anyTimes();
+		expect(sw.write(anyObject(Iterable.class))).andReturn(Collections.EMPTY_LIST).anyTimes();
 		expect(sw.getNumTables()).andStubReturn((short)0);
 		sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true);
 		expectLastCall().once();
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer10Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer10Test.java
index be752d0246f9075751fae5d2f321a7ee4af85ac7..d940ed4d55ee6da5e922d871a8319a6ce2437457 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer10Test.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer10Test.java
@@ -16,11 +16,12 @@ import java.util.EnumSet;
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.Matchers;
 import org.junit.Test;
+
 import net.floodlightcontroller.core.IOFSwitchBackend;
-import net.floodlightcontroller.core.OFConnection;
 import net.floodlightcontroller.core.SwitchDescription;
 import net.floodlightcontroller.core.internal.OFSwitchAppHandshakePlugin.PluginResultType;
 import net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.WaitAppHandshakeState;
+
 import org.projectfloodlight.openflow.protocol.OFActionType;
 import org.projectfloodlight.openflow.protocol.OFCapabilities;
 import org.projectfloodlight.openflow.protocol.OFControllerRole;
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java
index 9cb4c82167651f09f48588bc085e460103220e23..7f8393f65b704c26e3b865c93a2518494439e7f3 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandlerVer13Test.java
@@ -18,7 +18,6 @@ import org.hamcrest.Matchers;
 import org.junit.Test;
 
 import net.floodlightcontroller.core.IOFSwitchBackend;
-import net.floodlightcontroller.core.OFConnection;
 import net.floodlightcontroller.core.SwitchDescription;
 import net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.WaitAppHandshakeState;
 import net.floodlightcontroller.core.internal.OFSwitchHandshakeHandler.WaitTableFeaturesReplyState;
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java
index 62d43ab96a1305cb89e0df40ae14df5ee9f89fd2..6f5312ae2670344d91791bd4b87292a4ee5078e1 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java
@@ -35,7 +35,7 @@ import static org.junit.Assert.fail;
 
 import java.util.List;
 
-import org.jboss.netty.util.Timer;
+import io.netty.util.Timer;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -50,8 +50,6 @@ import net.floodlightcontroller.core.IOFSwitchListener;
 import net.floodlightcontroller.core.IShutdownListener;
 import net.floodlightcontroller.core.IShutdownService;
 import net.floodlightcontroller.core.LogicalOFMessageCategory;
-import net.floodlightcontroller.core.NullConnection;
-import net.floodlightcontroller.core.OFSwitch;
 import net.floodlightcontroller.core.PortChangeType;
 import net.floodlightcontroller.core.SwitchDescription;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchTest.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchTest.java
index b5ca01ed6884656e520c40ccd5cc284e88eae5f9..483aab250f55cf2b5b3b7445ee75c452eda94e87 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchTest.java
@@ -29,12 +29,13 @@ import java.util.List;
 import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
+
 import net.floodlightcontroller.core.IOFSwitchBackend;
-import net.floodlightcontroller.core.OFSwitch;
 import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted;
 import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted;
 import net.floodlightcontroller.core.SwitchDriverSubHandshakeNotStarted;
 import net.floodlightcontroller.core.util.URIUtil;
+
 import org.projectfloodlight.openflow.protocol.OFBsnControllerConnection;
 import org.projectfloodlight.openflow.protocol.OFBsnControllerConnectionState;
 import org.projectfloodlight.openflow.protocol.OFBsnControllerConnectionsReply;
@@ -48,116 +49,116 @@ import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFAuxId;
 
 public class OFSwitchTest {
-    protected OFSwitch sw;
-    protected OFFactory factory = OFFactories.getFactory(OFVersion.OF_13);
-
-    @Before
-    public void setUp() throws Exception {
-        MockOFConnection mockConnection = new MockOFConnection(DatapathId.of(1), OFAuxId.MAIN);
-        sw = new OFSwitch(mockConnection, OFFactories.getFactory(OFVersion.OF_10),
-                EasyMock.createMock(IOFSwitchManager.class), DatapathId.of(1));
-    }
-
-    @Test
-    public void testSetHARoleReply() {
-        sw.setControllerRole(OFControllerRole.ROLE_MASTER);
-        assertEquals(OFControllerRole.ROLE_MASTER, sw.getControllerRole());
-
-        sw.setControllerRole(OFControllerRole.ROLE_EQUAL);
-        assertEquals(OFControllerRole.ROLE_EQUAL, sw.getControllerRole());
-
-        sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
-        assertEquals(OFControllerRole.ROLE_SLAVE, sw.getControllerRole());
-    }
-
-    @Test
-    public void testSubHandshake() {
-        OFFactory factory = OFFactories.getFactory(OFVersion.OF_10);
-        OFMessage m = factory.buildNiciraControllerRoleReply()
-                .setXid(1)
-                .setRole(OFNiciraControllerRole.ROLE_MASTER)
-                .build();
-        // test exceptions before handshake is started
-        try {
-            sw.processDriverHandshakeMessage(m);
-            fail("expected exception not thrown");
-        } catch (SwitchDriverSubHandshakeNotStarted e) { /* expected */ }
-        try {
-            sw.isDriverHandshakeComplete();
-            fail("expected exception not thrown");
-        } catch (SwitchDriverSubHandshakeNotStarted e) { /* expected */ }
-
-        // start the handshake -- it should immediately complete
-        sw.startDriverHandshake();
-        assertTrue("Handshake should be complete",
-                   sw.isDriverHandshakeComplete());
-
-        // test exceptions after handshake is completed
-        try {
-            sw.processDriverHandshakeMessage(m);
-            fail("expected exception not thrown");
-        } catch (SwitchDriverSubHandshakeCompleted e) { /* expected */ }
-        try {
-            sw.startDriverHandshake();
-            fail("Expected exception not thrown");
-        } catch (SwitchDriverSubHandshakeAlreadyStarted e) { /* expected */ }
-    }
-
-    /**
-     * Helper to load controller connection messages into a switch for testing.
-     * @param sw the switch to insert the message on
-     * @param role the role for the controller connection message
-     * @param state the state for the controller connection message
-     * @param uri the URI for the controller connection message
-     */
-    public void updateControllerConnections(IOFSwitchBackend sw, OFControllerRole role1, OFBsnControllerConnectionState state1, String uri1
-                                            ,  OFControllerRole role2, OFBsnControllerConnectionState state2, String uri2) {
-        OFBsnControllerConnection connection1 = factory.buildBsnControllerConnection()
-                .setAuxiliaryId(OFAuxId.MAIN)
-                .setRole(role1)
-                .setState(state1)
-                .setUri(uri1)
-                .build();
-
-        OFBsnControllerConnection connection2 = factory.buildBsnControllerConnection()
-                .setAuxiliaryId(OFAuxId.MAIN)
-                .setRole(role2)
-                .setState(state2)
-                .setUri(uri2)
-                .build();
-
-        List<OFBsnControllerConnection> connections = new ArrayList<OFBsnControllerConnection>();
-        connections.add(connection1);
-        connections.add(connection2);
-
-        OFBsnControllerConnectionsReply reply = factory.buildBsnControllerConnectionsReply()
-                .setConnections(connections)
-                .build();
-
-        sw.updateControllerConnections(reply);
-    }
-
-    /**
-     * This test ensures that the switch accurately determined if another master
-     * exists in the cluster by examining the controller connections it has.
-     */
-    @Test
-    public void testHasAnotherMaster() {
-        URI cokeUri = URIUtil.createURI("1.2.3.4", 6653);
-        InetSocketAddress address = (InetSocketAddress) sw.getConnection(OFAuxId.MAIN).getLocalInetAddress();
-        URI pepsiUri = URIUtil.createURI(address.getHostName(), address.getPort());
-
-        updateControllerConnections(sw, OFControllerRole.ROLE_SLAVE, OFBsnControllerConnectionState.BSN_CONTROLLER_CONNECTION_STATE_CONNECTED, cokeUri.toString(),
-                                    OFControllerRole.ROLE_MASTER, OFBsnControllerConnectionState.BSN_CONTROLLER_CONNECTION_STATE_CONNECTED, pepsiUri.toString());
-
-        // From the perspective of pepsi, the cluster currently does NOT have another master controller
-        assertFalse(sw.hasAnotherMaster());
-
-        // Switch the controller connections so that pepsi is no longer master
-        updateControllerConnections(sw, OFControllerRole.ROLE_MASTER, OFBsnControllerConnectionState.BSN_CONTROLLER_CONNECTION_STATE_CONNECTED, cokeUri.toString(),
-                                    OFControllerRole.ROLE_SLAVE, OFBsnControllerConnectionState.BSN_CONTROLLER_CONNECTION_STATE_CONNECTED, pepsiUri.toString());
-
-        // From the perspective of pepsi, the cluster currently has another master controller
-        assertTrue(sw.hasAnotherMaster());
-    }
+	protected OFSwitch sw;
+	protected OFFactory factory = OFFactories.getFactory(OFVersion.OF_13);
+
+	@Before
+	public void setUp() throws Exception {
+		MockOFConnection mockConnection = new MockOFConnection(DatapathId.of(1), OFAuxId.MAIN);
+		sw = new OFSwitch(mockConnection, OFFactories.getFactory(OFVersion.OF_10),
+				EasyMock.createMock(IOFSwitchManager.class), DatapathId.of(1));
+	}
+
+	@Test
+	public void testSetHARoleReply() {
+		sw.setControllerRole(OFControllerRole.ROLE_MASTER);
+		assertEquals(OFControllerRole.ROLE_MASTER, sw.getControllerRole());
+
+		sw.setControllerRole(OFControllerRole.ROLE_EQUAL);
+		assertEquals(OFControllerRole.ROLE_EQUAL, sw.getControllerRole());
+
+		sw.setControllerRole(OFControllerRole.ROLE_SLAVE);
+		assertEquals(OFControllerRole.ROLE_SLAVE, sw.getControllerRole());
+	}
+
+	@Test
+	public void testSubHandshake() {
+		OFFactory factory = OFFactories.getFactory(OFVersion.OF_10);
+		OFMessage m = factory.buildNiciraControllerRoleReply()
+				.setXid(1)
+				.setRole(OFNiciraControllerRole.ROLE_MASTER)
+				.build();
+		// test exceptions before handshake is started
+		try {
+			sw.processDriverHandshakeMessage(m);
+			fail("expected exception not thrown");
+		} catch (SwitchDriverSubHandshakeNotStarted e) { /* expected */ }
+		try {
+			sw.isDriverHandshakeComplete();
+			fail("expected exception not thrown");
+		} catch (SwitchDriverSubHandshakeNotStarted e) { /* expected */ }
+
+		// start the handshake -- it should immediately complete
+		sw.startDriverHandshake();
+		assertTrue("Handshake should be complete",
+				sw.isDriverHandshakeComplete());
+
+		// test exceptions after handshake is completed
+		try {
+			sw.processDriverHandshakeMessage(m);
+			fail("expected exception not thrown");
+		} catch (SwitchDriverSubHandshakeCompleted e) { /* expected */ }
+		try {
+			sw.startDriverHandshake();
+			fail("Expected exception not thrown");
+		} catch (SwitchDriverSubHandshakeAlreadyStarted e) { /* expected */ }
+	}
+
+	/**
+	 * Helper to load controller connection messages into a switch for testing.
+	 * @param sw the switch to insert the message on
+	 * @param role the role for the controller connection message
+	 * @param state the state for the controller connection message
+	 * @param uri the URI for the controller connection message
+	 */
+	public void updateControllerConnections(IOFSwitchBackend sw, OFControllerRole role1, OFBsnControllerConnectionState state1, String uri1
+			,  OFControllerRole role2, OFBsnControllerConnectionState state2, String uri2) {
+		OFBsnControllerConnection connection1 = factory.buildBsnControllerConnection()
+				.setAuxiliaryId(OFAuxId.MAIN)
+				.setRole(role1)
+				.setState(state1)
+				.setUri(uri1)
+				.build();
+
+		OFBsnControllerConnection connection2 = factory.buildBsnControllerConnection()
+				.setAuxiliaryId(OFAuxId.MAIN)
+				.setRole(role2)
+				.setState(state2)
+				.setUri(uri2)
+				.build();
+
+		List<OFBsnControllerConnection> connections = new ArrayList<OFBsnControllerConnection>();
+		connections.add(connection1);
+		connections.add(connection2);
+
+		OFBsnControllerConnectionsReply reply = factory.buildBsnControllerConnectionsReply()
+				.setConnections(connections)
+				.build();
+
+		sw.updateControllerConnections(reply);
+	}
+
+	/**
+	 * This test ensures that the switch accurately determined if another master
+	 * exists in the cluster by examining the controller connections it has.
+	 */
+	@Test
+	public void testHasAnotherMaster() {
+		URI cokeUri = URIUtil.createURI("1.2.3.4", 6653);
+		InetSocketAddress address = (InetSocketAddress) sw.getConnection(OFAuxId.MAIN).getLocalInetAddress();
+		URI pepsiUri = URIUtil.createURI(address.getHostName(), address.getPort());
+
+		updateControllerConnections(sw, OFControllerRole.ROLE_SLAVE, OFBsnControllerConnectionState.BSN_CONTROLLER_CONNECTION_STATE_CONNECTED, cokeUri.toString(),
+				OFControllerRole.ROLE_MASTER, OFBsnControllerConnectionState.BSN_CONTROLLER_CONNECTION_STATE_CONNECTED, pepsiUri.toString());
+
+		// From the perspective of pepsi, the cluster currently does NOT have another master controller
+		assertFalse(sw.hasAnotherMaster());
+
+		// Switch the controller connections so that pepsi is no longer master
+		updateControllerConnections(sw, OFControllerRole.ROLE_MASTER, OFBsnControllerConnectionState.BSN_CONTROLLER_CONNECTION_STATE_CONNECTED, cokeUri.toString(),
+				OFControllerRole.ROLE_SLAVE, OFBsnControllerConnectionState.BSN_CONTROLLER_CONNECTION_STATE_CONNECTED, pepsiUri.toString());
+
+		// From the perspective of pepsi, the cluster currently has another master controller
+		assertTrue(sw.hasAnotherMaster());
+	}
 }
diff --git a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
index 4f36d16ef79b20b013c883b56fc8b97655119402..d019d624a3f638ecd17b4b62c1c4e8af9b4910be 100644
--- a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
+++ b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
@@ -36,8 +36,7 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
-import org.jboss.netty.util.Timer;
-
+import io.netty.util.Timer;
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.HAListenerTypeMarker;
 import net.floodlightcontroller.core.HARole;
@@ -61,6 +60,8 @@ 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;
 
@@ -76,8 +77,8 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
     protected ConcurrentMap<OFType, ListenerDispatcher<OFType,IOFMessageListener>> listeners;
     protected ListenerDispatcher<HAListenerTypeMarker, IHAListener> haListeners;
     private HARole role;
-    private final String openFlowHostname = "127.0.0.1";
-    private final int openFlowPort = 6653;
+    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;
@@ -385,12 +386,12 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
     }
 
     @Override
-    public String getOFHostname() {
+    public Set<IPv4Address> getOFAddresses() {
         return openFlowHostname;
     }
 
     @Override
-    public int getOFPort() {
+    public TransportPort getOFPort() {
         return openFlowPort;
     }
 
diff --git a/src/test/java/net/floodlightcontroller/core/test/TestEventLoop.java b/src/test/java/net/floodlightcontroller/core/test/TestEventLoop.java
new file mode 100644
index 0000000000000000000000000000000000000000..b31725e6762a47a4b35ce67f059ac27979b46da4
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/core/test/TestEventLoop.java
@@ -0,0 +1,134 @@
+package net.floodlightcontroller.core.test;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.collect.ImmutableList;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.DefaultChannelPromise;
+import io.netty.channel.EventLoop;
+import io.netty.channel.EventLoopGroup;
+import io.netty.util.concurrent.AbstractScheduledEventExecutor;
+import io.netty.util.concurrent.Future;
+
+public final class TestEventLoop extends AbstractScheduledEventExecutor implements EventLoop {
+
+    private final Queue<Runnable> tasks = new ArrayDeque<Runnable>(2);
+
+    @Override
+    public void execute(Runnable command) {
+        if (command == null) {
+            throw new NullPointerException("command");
+        }
+        tasks.add(command);
+    }
+
+    public void runTasks() {
+        for (;;) {
+            Runnable task = tasks.poll();
+            if (task == null) {
+                break;
+            }
+
+            task.run();
+        }
+    }
+
+    long runScheduledTasks() {
+        long time = AbstractScheduledEventExecutor.nanoTime();
+        for (;;) {
+            Runnable task = pollScheduledTask(time);
+            if (task == null) {
+                return nextScheduledTaskNano();
+            }
+
+            task.run();
+        }
+    }
+
+    long nextScheduledTask() {
+        return nextScheduledTaskNano();
+    }
+
+    @Override
+    protected void cancelScheduledTasks() {
+        super.cancelScheduledTasks();
+    }
+
+    @Override
+    public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Future<?> terminationFuture() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @Deprecated
+    public void shutdown() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isShuttingDown() {
+        return false;
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return false;
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return false;
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit)
+            throws InterruptedException {
+        Thread.sleep(unit.toMillis(timeout));
+        return false;
+    }
+
+    @Override
+    public ChannelFuture register(Channel channel) {
+        return register(channel, new DefaultChannelPromise(channel, this));
+    }
+
+    @Override
+    public ChannelFuture register(Channel channel, ChannelPromise promise) {
+        channel.unsafe().register(this, promise);
+        return promise;
+    }
+
+    @Override
+    public boolean inEventLoop() {
+        return true;
+    }
+
+    @Override
+    public boolean inEventLoop(Thread thread) {
+        return true;
+    }
+
+    @Override
+    public EventLoop next() {
+        return this;
+    }
+
+    @Override
+    public EventLoopGroup parent() {
+        return this;
+    }
+
+    public ImmutableList<Runnable> getQueuedTasks() {
+        return ImmutableList.copyOf(tasks);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/debugcounter/OFConnectionCountersTest.java b/src/test/java/net/floodlightcontroller/debugcounter/OFConnectionCountersTest.java
index c82eb132c050eef6b9acedc0cde5936dd2545154..46984522e60e9af43a471a42e8cc5c6f759c3deb 100644
--- a/src/test/java/net/floodlightcontroller/debugcounter/OFConnectionCountersTest.java
+++ b/src/test/java/net/floodlightcontroller/debugcounter/OFConnectionCountersTest.java
@@ -7,7 +7,7 @@ import java.util.ArrayList;
 import org.junit.Before;
 import org.junit.Test;
 
-import net.floodlightcontroller.core.OFConnectionCounters;
+import net.floodlightcontroller.core.internal.OFConnectionCounters;
 
 import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
 import org.projectfloodlight.openflow.protocol.OFAsyncGetRequest;
diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
index 8c84298cd564b869a1a156a13d2274d328795464..fb1798028b835d52a8577b94ac736d25b1c2353d 100644
--- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
+++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java
@@ -31,6 +31,7 @@ import java.util.Set;
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.SwitchDescription;
 import net.floodlightcontroller.core.internal.IOFSwitchService;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.test.MockThreadPoolService;
@@ -67,6 +68,7 @@ import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFMessage;
@@ -99,6 +101,7 @@ public class ForwardingTest extends FloodlightTestCase {
 	protected MockThreadPoolService threadPool;
 	protected IOFSwitch sw1, sw2;
 	protected OFFeaturesReply swFeatures;
+	protected OFDescStatsReply swDescription;
 	protected IDevice srcDevice, dstDevice1, dstDevice2; /* reuse for IPv4 and IPv6 */
 	protected OFPacketIn packetIn;
 	protected OFPacketIn packetInIPv6;
@@ -162,6 +165,7 @@ public class ForwardingTest extends FloodlightTestCase {
 		entityClassifier.startUp(fmc);
 		verify(topology);
 
+		swDescription = factory.buildDescStatsReply().build();
 		swFeatures = factory.buildFeaturesReply().setNBuffers(1000).build();
 		// Mock switches
 		sw1 = EasyMock.createMock(IOFSwitch.class);
@@ -177,6 +181,12 @@ public class ForwardingTest extends FloodlightTestCase {
 		expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
 
 		expect(sw2.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
+		
+		expect(sw1.getSwitchDescription()).andReturn(new SwitchDescription(swDescription)).anyTimes();
+		expect(sw2.getSwitchDescription()).andReturn(new SwitchDescription(swDescription)).anyTimes();
+		
+		expect(sw1.isActive()).andReturn(true).anyTimes();
+		expect(sw2.isActive()).andReturn(true).anyTimes();
 
 		// Load the switch map
 		Map<DatapathId, IOFSwitch> switches = new HashMap<DatapathId, IOFSwitch>();
@@ -450,10 +460,8 @@ public class ForwardingTest extends FloodlightTestCase {
 				.build();
 		OFFlowMod fm2 = fm1.createBuilder().build();
 
-		sw1.write(capture(wc1));
-		expectLastCall().anyTimes();
-		sw2.write(capture(wc2));
-		expectLastCall().anyTimes();
+		expect(sw1.write(capture(wc1))).andReturn(true).anyTimes();
+		expect(sw2.write(capture(wc2))).andReturn(true).anyTimes();
 
 		reset(topology);
 		expect(topology.getOpenflowDomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
@@ -522,10 +530,8 @@ public class ForwardingTest extends FloodlightTestCase {
 				.build();
 		OFFlowMod fm2 = fm1.createBuilder().build();
 
-		sw1.write(capture(wc1));
-		expectLastCall().anyTimes();
-		sw2.write(capture(wc2));
-		expectLastCall().anyTimes();
+		expect(sw1.write(capture(wc1))).andReturn(true).anyTimes();
+		expect(sw2.write(capture(wc2))).andReturn(true).anyTimes();
 
 		reset(topology);
 		expect(topology.getOpenflowDomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
@@ -590,10 +596,8 @@ public class ForwardingTest extends FloodlightTestCase {
 				.build();
 
 		// Record expected packet-outs/flow-mods
-		sw1.write(capture(wc1));
-		expectLastCall().once();
-		sw1.write(capture(wc2));
-		expectLastCall().once();
+		expect(sw1.write(capture(wc1))).andReturn(true).once();
+		expect(sw1.write(capture(wc2))).andReturn(true).once();
 
 		reset(topology);
 		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
@@ -646,10 +650,8 @@ public class ForwardingTest extends FloodlightTestCase {
 				.build();
 
 		// Record expected packet-outs/flow-mods
-		sw1.write(capture(wc1));
-		expectLastCall().once();
-		sw1.write(capture(wc2));
-		expectLastCall().once();
+		expect(sw1.write(capture(wc1))).andReturn(true).once();
+		expect(sw1.write(capture(wc2))).andReturn(true).once();
 
 		reset(topology);
 		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
@@ -751,8 +753,7 @@ public class ForwardingTest extends FloodlightTestCase {
 				.andReturn(true)
 				.anyTimes();
 		expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)).andReturn(true).anyTimes();
-		sw1.write(capture(wc1));
-		expectLastCall().once();
+		expect(sw1.write(capture(wc1))).andReturn(true).once();
 		replay(sw1, sw2, routingEngine, topology);
 		forwarding.receive(sw1, this.packetIn, cntx);
 		verify(sw1, sw2, routingEngine);
@@ -785,8 +786,7 @@ public class ForwardingTest extends FloodlightTestCase {
 		expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD))
 		.andReturn(true).anyTimes();
 		// Reset XID to expected (dependent on prior unit tests)
-		sw1.write(capture(wc1));
-		expectLastCall().once();
+		expect(sw1.write(capture(wc1))).andReturn(true).once();
 		replay(sw1, sw2, routingEngine, topology);
 		forwarding.receive(sw1, this.packetInIPv6, cntx);
 		verify(sw1, sw2, routingEngine);
diff --git a/src/test/java/net/floodlightcontroller/hub/HubTest.java b/src/test/java/net/floodlightcontroller/hub/HubTest.java
index 6a0a2dde83c80d9baf104afed0b176f3a00d546c..6bc1c98984d123ef3342de16dac4021bfca7a263 100644
--- a/src/test/java/net/floodlightcontroller/hub/HubTest.java
+++ b/src/test/java/net/floodlightcontroller/hub/HubTest.java
@@ -19,6 +19,7 @@ package net.floodlightcontroller.hub;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.verify;
 import static org.easymock.EasyMock.capture;
 import static org.junit.Assert.*;
@@ -122,7 +123,7 @@ public class HubTest extends FloodlightTestCase {
         
         Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
         
-        mockSwitch.write(capture(wc1));
+        expect(mockSwitch.write(capture(wc1))).andReturn(true).anyTimes();
 
         // Start recording the replay on the mocks
         replay(mockSwitch);
@@ -163,7 +164,7 @@ public class HubTest extends FloodlightTestCase {
         IOFSwitch mockSwitch = createMock(IOFSwitch.class);
         EasyMock.expect(mockSwitch.getOFFactory()).andReturn(OFFactories.getFactory(OFVersion.OF_13)).anyTimes();
         Capture<OFPacketOut> wc1 = new Capture<OFPacketOut>(CaptureType.ALL);
-        mockSwitch.write(capture(wc1));
+        expect(mockSwitch.write(capture(wc1))).andReturn(true).anyTimes();
 
         // Start recording the replay on the mocks
         replay(mockSwitch);
diff --git a/src/test/java/net/floodlightcontroller/learningswitch/LearningSwitchTest.java b/src/test/java/net/floodlightcontroller/learningswitch/LearningSwitchTest.java
index b2754b2ea73b0b14743c7432648c45c5c7655aae..fd397c461a006fb0ce65d9854977a633fbc8fca8 100644
--- a/src/test/java/net/floodlightcontroller/learningswitch/LearningSwitchTest.java
+++ b/src/test/java/net/floodlightcontroller/learningswitch/LearningSwitchTest.java
@@ -190,8 +190,7 @@ public class LearningSwitchTest extends FloodlightTestCase {
         IOFSwitch mockSwitch = createMock(IOFSwitch.class);
         expect(mockSwitch.getId()).andReturn(DatapathId.of("00:11:22:33:44:55:66:77")).anyTimes();
         expect(mockSwitch.getOFFactory()).andReturn(factory).anyTimes();
-        mockSwitch.write(EasyMock.capture(wc1)); // expect po
-        EasyMock.expectLastCall().once();
+        expect(mockSwitch.write(EasyMock.capture(wc1))).andReturn(true).once(); // expect po
 
         // Start recording the replay on the mocks
         replay(mockSwitch);
@@ -269,12 +268,9 @@ public class LearningSwitchTest extends FloodlightTestCase {
         expect(mockSwitch.getBuffers()).andReturn((long)100).anyTimes();
         expect(mockSwitch.getOFFactory()).andReturn(factory).anyTimes();
         
-        mockSwitch.write(EasyMock.capture(wc1)); // expect packetOut
-        EasyMock.expectLastCall().once();
-        mockSwitch.write(EasyMock.capture(wc2)); // expect fm1
-        EasyMock.expectLastCall().once();
-        mockSwitch.write(EasyMock.capture(wc3)); // expect fm2
-        EasyMock.expectLastCall().once();
+        expect(mockSwitch.write(EasyMock.capture(wc1))).andReturn(true).once(); // expect packetOut
+        expect(mockSwitch.write(EasyMock.capture(wc2))).andReturn(true).once(); // expect fm1
+        expect(mockSwitch.write(EasyMock.capture(wc3))).andReturn(true).once(); // expect fm2
 
         // Start recording the replay on the mocks
         replay(mockSwitch);
diff --git a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java
index a4c8d244785fcccf952d5db5133bd7e01e72264f..89638f378e66be3bb5c21d6ea7b95629ff7416e1 100644
--- a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java
+++ b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java
@@ -21,7 +21,6 @@ import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createNiceMock;
 import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
 import static org.junit.Assert.*;
@@ -497,8 +496,7 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase {
         expect(sw1.getPort(OFPort.of(EasyMock.anyInt()))).andReturn(ofpp).anyTimes();
         expect(sw1.getOFFactory()).andReturn(OFFactories.getFactory(OFVersion.OF_13)).anyTimes();
         expect(sw1.getLatency()).andReturn(U64.ZERO).anyTimes();
-        sw1.write(capture(wc));
-        expectLastCall().anyTimes();
+        expect(sw1.write(capture(wc))).andReturn(true).anyTimes();
         replay(sw1);
 
         linkDiscovery.switchActivated(sw1.getId());
diff --git a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
index f19ce3bff93ff051ce38c8853c1b375246e0a1a2..f8c0101af6b2b5e534f232c0f566d4984756d858 100644
--- a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
+++ b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
@@ -446,10 +446,7 @@ public class LoadBalancerTest extends FloodlightTestCase {
 		expect(sw1.getId()).andReturn(DatapathId.of(1L)).anyTimes();
 		expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
 		expect(sw1.getOFFactory()).andReturn(factory).anyTimes();
-		sw1.write(capture(wc1));
-		expectLastCall().anyTimes();
-		sw1.flush();
-		expectLastCall().anyTimes();
+		expect(sw1.write(capture(wc1))).andReturn(true).anyTimes();
 		
 		replay(sw1);
 		sfp.switchAdded(DatapathId.of(1L));
diff --git a/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java b/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java
index 51b2319f19053890ae402247eaf4b05a83f6bebe..25f8f4ea1ade6578f2ee1361f641f1ded33d3fdb 100644
--- a/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java
+++ b/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java
@@ -203,12 +203,8 @@ public class StaticFlowTests extends FloodlightTestCase {
 		writeCapture = new Capture<OFMessage>(CaptureType.ALL);
 		writeCaptureList = new Capture<List<OFMessage>>(CaptureType.ALL);
 
-		mockSwitch.write(capture(writeCapture));
-		expectLastCall().anyTimes();
-		mockSwitch.write(capture(writeCaptureList));
-		expectLastCall().anyTimes();
-		mockSwitch.flush();
-		expectLastCall().anyTimes();
+		expect(mockSwitch.write(capture(writeCapture))).andReturn(true).anyTimes();
+		expect(mockSwitch.write(capture(writeCaptureList))).andReturn(Collections.<OFMessage>emptyList()).anyTimes();
 		expect(mockSwitch.getOFFactory()).andReturn(factory).anyTimes();
 		replay(mockSwitch);
 
@@ -249,12 +245,8 @@ public class StaticFlowTests extends FloodlightTestCase {
 
 		// if someone calls getId(), return this dpid instead
 		resetToNice(mockSwitch);
-		mockSwitch.write(capture(writeCapture));
-		expectLastCall().anyTimes();
-		mockSwitch.write(capture(writeCaptureList));
-		expectLastCall().anyTimes();
-		mockSwitch.flush();
-		expectLastCall().anyTimes();
+		expect(mockSwitch.write(capture(writeCapture))).andReturn(true).anyTimes();
+		expect(mockSwitch.write(capture(writeCaptureList))).andReturn(Collections.<OFMessage> emptyList()).anyTimes();
 		expect(mockSwitch.getOFFactory()).andReturn(factory).anyTimes();
 		expect(mockSwitch.getId()).andReturn(DatapathId.of(dpid)).anyTimes();
 		replay(mockSwitch);
diff --git a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java
index c4005b32aaec719cfd0f4f43732bacc941f75bb3..39569240cc8603be553fd5507e638d889a5c3429 100644
--- a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java
+++ b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.*;
 
 import java.net.SocketAddress;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -28,8 +29,8 @@ import java.util.Set;
 import net.floodlightcontroller.core.IOFConnection;
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.core.LogicalOFMessageCategory;
-import net.floodlightcontroller.core.OFConnection;
 import net.floodlightcontroller.core.SwitchDescription;
+import net.floodlightcontroller.core.internal.OFConnection;
 import net.floodlightcontroller.core.internal.TableFeatures;
 
 import org.projectfloodlight.openflow.protocol.OFActionType;
@@ -160,11 +161,6 @@ public class OFMessageDamperMockSwitch implements IOFSwitch {
         return null;
     }
 
-    @Override
-    public void flush() {
-        assertTrue("Unexpected method call", false);
-    }
-
     @Override
     public long getBuffers() {
         fail("Unexpected method call");
@@ -208,14 +204,14 @@ public class OFMessageDamperMockSwitch implements IOFSwitch {
     }
 
 	@Override
-	public void write(OFMessage m) {
+	public boolean write(OFMessage m) {
 		writtenMessage = m;
-		// TODO Auto-generated method stub	
+		return true;
 	}
 
 	@Override
-	public void write(Iterable<OFMessage> msglist) {
-		// TODO Auto-generated method stub
+	public Collection<OFMessage> write(Iterable<OFMessage> msgList) {
+		return Collections.emptyList();
 	}
 
 	@Override
@@ -287,16 +283,14 @@ public class OFMessageDamperMockSwitch implements IOFSwitch {
 	}
 
 	@Override
-	public void write(OFMessage m, LogicalOFMessageCategory category) {
-		// TODO Auto-generated method stub
-		
+	public boolean write(OFMessage m, LogicalOFMessageCategory category) {
+		return true;
 	}
 
 	@Override
-	public void write(Iterable<OFMessage> msglist,
+	public Collection<OFMessage> write(Iterable<OFMessage> msgList,
 			LogicalOFMessageCategory category) {
-		// TODO Auto-generated method stub
-		
+		return Collections.emptyList();
 	}
 
 	@Override
diff --git a/src/test/java/org/sdnplatform/sync/internal/BootstrapTest.java b/src/test/java/org/sdnplatform/sync/internal/BootstrapTest.java
index 4aeb57ac9fd692fa07b8ab2dbe3659c47db2933a..0bd1764f7290728602cf430c2d15fbe9733687f6 100644
--- a/src/test/java/org/sdnplatform/sync/internal/BootstrapTest.java
+++ b/src/test/java/org/sdnplatform/sync/internal/BootstrapTest.java
@@ -110,7 +110,7 @@ public class BootstrapTest {
             unsyncStore.put("seeds", curSeed);
 
             waitForValue(unsyncStore, "localNodeId", null, 
-                         3000, "unsyncStore" + i);
+                         10000, "unsyncStore" + i);
             short nodeId = 
                     Short.parseShort(unsyncStore.getValue("localNodeId"));
             Node node = nodeStore.getValue(nodeId);