Added option to initiate connection to device 77/14977/7
authorMartin Uhlir <martin.uhlir@pantheon.sk>
Fri, 6 Feb 2015 14:41:38 +0000 (15:41 +0100)
committerMartin Uhlir <martin.uhlir@pantheon.sk>
Wed, 11 Feb 2015 15:16:17 +0000 (16:16 +0100)
 - as this feature was missing (according to OF specification)
 - added integration test

Change-Id: I0f7346195999170a74975ee3438bc948fd685e61
Signed-off-by: Martin Uhlir <martin.uhlir@pantheon.sk>
openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ConnectionInitializer.java [new file with mode: 0644]
openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/SwitchConnectionProviderImpl.java
openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpChannelInitializer.java
openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpConnectionInitializer.java [new file with mode: 0644]
openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpHandler.java
openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/TcpHandlerTest.java
openflow-protocol-it/src/test/java/org/opendaylight/openflowjava/protocol/it/integration/IntegrationTest.java
openflow-protocol-it/src/test/java/org/opendaylight/openflowjava/protocol/it/integration/MockPlugin.java
openflow-protocol-spi/src/main/java/org/opendaylight/openflowjava/protocol/spi/connection/SwitchConnectionProvider.java
simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/ListeningSimpleClient.java [new file with mode: 0644]

diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ConnectionInitializer.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ConnectionInitializer.java
new file mode 100644 (file)
index 0000000..4959eda
--- /dev/null
@@ -0,0 +1,16 @@
+package org.opendaylight.openflowjava.protocol.impl.core;
+
+/**
+ * @author martin.uhlir
+ *
+ */
+public interface ConnectionInitializer {
+
+    /**
+     * Initiates connection towards device
+     * @param host - host IP
+     * @param port - port number
+     */
+    void initiateConnection(String host, int port);
+
+}
index 9e8a7a185946b893d54556dd1cd94bc174cb33a9..92f058276abd6850ffe921a9b86762675c6cc991 100644 (file)
@@ -9,6 +9,8 @@
 
 package org.opendaylight.openflowjava.protocol.impl.core;
 
+import io.netty.channel.nio.NioEventLoopGroup;
+
 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionConfiguration;
 import org.opendaylight.openflowjava.protocol.api.connection.SwitchConnectionHandler;
 import org.opendaylight.openflowjava.protocol.api.extensibility.DeserializerRegistry;
@@ -58,7 +60,7 @@ import com.google.common.util.concurrent.SettableFuture;
  * @author mirehak
  * @author michal.polkorab
  */
-public class SwitchConnectionProviderImpl implements SwitchConnectionProvider {
+public class SwitchConnectionProviderImpl implements SwitchConnectionProvider, ConnectionInitializer {
 
     private static final Logger LOGGER = LoggerFactory
             .getLogger(SwitchConnectionProviderImpl.class);
@@ -69,6 +71,7 @@ public class SwitchConnectionProviderImpl implements SwitchConnectionProvider {
     private SerializerRegistry serializerRegistry;
     private DeserializerRegistry deserializerRegistry;
     private DeserializationFactory deserializationFactory;
+    private TcpConnectionInitializer connectionInitializer;
 
     /** Constructor */
     public SwitchConnectionProviderImpl() {
@@ -137,7 +140,14 @@ public class SwitchConnectionProviderImpl implements SwitchConnectionProvider {
         TransportProtocol transportProtocol = (TransportProtocol) connConfig.getTransferProtocol();
         if (transportProtocol.equals(TransportProtocol.TCP) || transportProtocol.equals(TransportProtocol.TLS)) {
             server = new TcpHandler(connConfig.getAddress(), connConfig.getPort());
-            ((TcpHandler) server).setChannelInitializer(factory.createPublishingChannelInitializer());
+            TcpChannelInitializer channelInitializer = factory.createPublishingChannelInitializer();
+            ((TcpHandler) server).setChannelInitializer(channelInitializer);
+            ((TcpHandler) server).initiateEventLoopGroups(connConfig.getThreadConfiguration());
+
+            NioEventLoopGroup workerGroupFromTcpHandler = ((TcpHandler) server).getWorkerGroup();
+            connectionInitializer = new TcpConnectionInitializer(workerGroupFromTcpHandler);
+            connectionInitializer.setChannelInitializer(channelInitializer);
+            connectionInitializer.run();
         } else if (transportProtocol.equals(TransportProtocol.UDP)){
             server = new UdpHandler(connConfig.getAddress(), connConfig.getPort());
             ((UdpHandler) server).setChannelInitializer(factory.createUdpChannelInitializer());
@@ -265,4 +275,10 @@ public class SwitchConnectionProviderImpl implements SwitchConnectionProvider {
             OFSerializer<MeterBandExperimenterCase> serializer) {
         serializerRegistry.registerSerializer(key, serializer);
     }
-}
\ No newline at end of file
+
+    @Override
+    public void initiateConnection(String host, int port) {
+        connectionInitializer.initiateConnection(host, port);
+    }
+
+}
index 07aab35488884c0b46fc049656eeff364e784ee3..8282f078e7b38b8738b24a62709411659490e437 100644 (file)
@@ -54,15 +54,18 @@ public class TcpChannelInitializer extends ProtocolChannelInitializer<SocketChan
 
     @Override
     protected void initChannel(final SocketChannel ch) {
-        InetAddress switchAddress = ch.remoteAddress().getAddress();
-        int port = ch.localAddress().getPort();
-        int remotePort = ch.remoteAddress().getPort();
-        LOGGER.debug("Incoming connection from (remote address): " + switchAddress.toString()
-                + ":" + remotePort + " --> :" + port);
-        if (!getSwitchConnectionHandler().accept(switchAddress)) {
-            ch.disconnect();
-            LOGGER.debug("Incoming connection rejected");
-            return;
+        if (ch.remoteAddress() != null) {
+            InetAddress switchAddress = ch.remoteAddress().getAddress();
+            int port = ch.localAddress().getPort();
+            int remotePort = ch.remoteAddress().getPort();
+            LOGGER.debug("Incoming connection from (remote address): " + switchAddress.toString()
+                    + ":" + remotePort + " --> :" + port);
+
+            if (!getSwitchConnectionHandler().accept(switchAddress)) {
+                ch.disconnect();
+                LOGGER.debug("Incoming connection rejected");
+                return;
+            }
         }
         LOGGER.debug("Incoming connection accepted - building pipeline");
         allChannels.add(ch);
diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpConnectionInitializer.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpConnectionInitializer.java
new file mode 100644 (file)
index 0000000..c4b0937
--- /dev/null
@@ -0,0 +1,80 @@
+package org.opendaylight.openflowjava.protocol.impl.core;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+
+import org.opendaylight.openflowjava.protocol.api.connection.ThreadConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * Initializes (TCP) connection to device
+ * @author martin.uhlir
+ *
+ */
+public class TcpConnectionInitializer implements ServerFacade,
+        ConnectionInitializer {
+
+    private static final Logger LOGGER = LoggerFactory
+            .getLogger(TcpConnectionInitializer.class);
+    private EventLoopGroup workerGroup;
+    private ThreadConfiguration threadConfig;
+
+    private TcpChannelInitializer channelInitializer;
+    private Bootstrap b;
+
+    /**
+     * Constructor
+     * @param workerGroup - shared worker group
+     */
+    public TcpConnectionInitializer(NioEventLoopGroup workerGroup) {
+        Preconditions.checkNotNull(workerGroup, "WorkerGroup can't be null");
+        this.workerGroup = workerGroup;
+    }
+
+    @Override
+    public void run() {
+        b = new Bootstrap();
+        b.group(workerGroup).channel(NioSocketChannel.class)
+            .handler(channelInitializer);
+    }
+
+    @Override
+    public ListenableFuture<Boolean> shutdown() {
+        final SettableFuture<Boolean> result = SettableFuture.create();
+        workerGroup.shutdownGracefully();
+        return result;
+    }
+
+    @Override
+    public ListenableFuture<Boolean> getIsOnlineFuture() {
+        return null;
+    }
+
+    @Override
+    public void setThreadConfig(ThreadConfiguration threadConfig) {
+        this.threadConfig = threadConfig;
+    }
+
+    @Override
+    public void initiateConnection(String host, int port) {
+        try {
+            b.connect(host, port).sync();
+        } catch (InterruptedException e) {
+            LOGGER.error("Unable to initiate connection", e);
+        }
+    }
+
+    /**
+     * @param channelInitializer
+     */
+    public void setChannelInitializer(TcpChannelInitializer channelInitializer) {
+        this.channelInitializer = channelInitializer;
+    }
+}
index 0c26354ea453557c1d6973d2b4d931040c0f93d6..48d5aa1462d42d0dc33bf96dc76bdd24ba4c9b09 100644 (file)
@@ -82,14 +82,6 @@ public class TcpHandler implements ServerFacade {
      */
     @Override
     public void run() {
-        if (threadConfig != null) {
-            bossGroup = new NioEventLoopGroup(threadConfig.getBossThreadCount());
-            workerGroup = new NioEventLoopGroup(threadConfig.getWorkerThreadCount());
-        } else {
-            bossGroup = new NioEventLoopGroup();
-            workerGroup = new NioEventLoopGroup();
-        }
-
         /*
          * We generally do not perform IO-unrelated tasks, so we want to have
          * all outstanding tasks completed before the executing thread goes
@@ -204,4 +196,26 @@ public class TcpHandler implements ServerFacade {
     public void setThreadConfig(ThreadConfiguration threadConfig) {
         this.threadConfig = threadConfig;
     }
+
+    /**
+     * Initiate event loop groups
+     * @param threadConfiguration number of threads to be created, if not specified in threadConfig
+     */
+    public void initiateEventLoopGroups(ThreadConfiguration threadConfiguration) {
+        if (threadConfiguration != null) {
+            bossGroup = new NioEventLoopGroup(threadConfiguration.getBossThreadCount());
+            workerGroup = new NioEventLoopGroup(threadConfiguration.getWorkerThreadCount());
+        } else {
+            bossGroup = new NioEventLoopGroup();
+            workerGroup = new NioEventLoopGroup();
+        }
+    }
+
+    /**
+     * @return workerGroup
+     */
+    public NioEventLoopGroup getWorkerGroup() {
+        return workerGroup;
+    }
+
 }
index 129a86681d1e188e9ac98fc583dc585dee29f5c0..6d103355904315729bafec4ac775f3f2b85f4c95 100644 (file)
@@ -123,6 +123,7 @@ public class TcpHandlerTest {
         try {
             tcpHandler = new TcpHandler(serverAddress, serverPort);
             tcpHandler.setChannelInitializer(mockChannelInitializer);
+            tcpHandler.initiateEventLoopGroups(null);
             tcpHandler.run();
         } catch (Exception e) {
             if (e instanceof BindException) {
@@ -150,7 +151,7 @@ public class TcpHandlerTest {
      */
     private Boolean startupServer() throws InterruptedException, IOException, ExecutionException {
         ListenableFuture<Boolean> online = tcpHandler.getIsOnlineFuture();
-
+        tcpHandler.initiateEventLoopGroups(null);
             (new Thread(tcpHandler)).start();
             int retry = 0;
             while (online.isDone() != true && retry++ < 20) {
index 6464a62fbdc6686b337b24666e329cf49c1576b9..3eedff52d3720b418da8424df562d1bdb32825f0 100644 (file)
@@ -12,7 +12,6 @@ import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Deque;
 import java.util.List;
-
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
@@ -21,6 +20,7 @@ import org.junit.Test;
 import org.opendaylight.openflowjava.protocol.api.connection.TlsConfiguration;
 import org.opendaylight.openflowjava.protocol.api.connection.TlsConfigurationImpl;
 import org.opendaylight.openflowjava.protocol.impl.clients.ClientEvent;
+import org.opendaylight.openflowjava.protocol.impl.clients.ListeningSimpleClient;
 import org.opendaylight.openflowjava.protocol.impl.clients.OFClient;
 import org.opendaylight.openflowjava.protocol.impl.clients.ScenarioFactory;
 import org.opendaylight.openflowjava.protocol.impl.clients.ScenarioHandler;
@@ -58,6 +58,9 @@ public class IntegrationTest {
     private SwitchConnectionProviderImpl switchConnectionProvider;
     private ConnectionConfigurationImpl connConfig;
 
+    private Thread t;
+
+    private enum ClientType {SIMPLE, LISTENING}
     /**
      * @param protocol communication protocol to be used during test
      * @throws Exception
@@ -110,7 +113,7 @@ public class IntegrationTest {
         int amountOfCLients = 1;
         Deque<ClientEvent> scenario = ScenarioFactory.createHandshakeScenario();
         ScenarioHandler handler = new ScenarioHandler(scenario);
-        List<OFClient> clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TCP);
+        List<OFClient> clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TCP, ClientType.SIMPLE);
         OFClient firstClient = clients.get(0);
         firstClient.getScenarioDone().get();
         Thread.sleep(1000);
@@ -128,7 +131,7 @@ public class IntegrationTest {
         int amountOfCLients = 1;
         Deque<ClientEvent> scenario = ScenarioFactory.createHandshakeScenario();
         ScenarioHandler handler = new ScenarioHandler(scenario);
-        List<OFClient> clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TLS);
+        List<OFClient> clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TLS, ClientType.SIMPLE);
         OFClient firstClient = clients.get(0);
         firstClient.getScenarioDone().get();
         Thread.sleep(1000);
@@ -150,7 +153,7 @@ public class IntegrationTest {
         scenario.addFirst(new SleepEvent(1000));
         scenario.addFirst(new WaitForMessageEvent(ByteBufUtils.hexStringToBytes("04 03 00 08 00 00 00 04")));
         ScenarioHandler handler = new ScenarioHandler(scenario);
-        List<OFClient> clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TCP);
+        List<OFClient> clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TCP, ClientType.SIMPLE);
         OFClient firstClient = clients.get(0);
         firstClient.getScenarioDone().get();
 
@@ -171,7 +174,7 @@ public class IntegrationTest {
         scenario.addFirst(new SleepEvent(1000));
         scenario.addFirst(new WaitForMessageEvent(ByteBufUtils.hexStringToBytes("04 03 00 08 00 00 00 04")));
         ScenarioHandler handler = new ScenarioHandler(scenario);
-        List<OFClient> clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TLS);
+        List<OFClient> clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TLS, ClientType.SIMPLE);
         OFClient firstClient = clients.get(0);
         firstClient.getScenarioDone().get();
 
@@ -192,7 +195,7 @@ public class IntegrationTest {
         scenario.addFirst(new SleepEvent(1000));
         scenario.addFirst(new WaitForMessageEvent(ByteBufUtils.hexStringToBytes("04 03 00 08 00 00 00 04")));
         ScenarioHandler handler = new ScenarioHandler(scenario);
-        List<OFClient> clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.UDP);
+        List<OFClient> clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.UDP, ClientType.SIMPLE);
         OFClient firstClient = clients.get(0);
         firstClient.getScenarioDone().get();
 
@@ -215,23 +218,35 @@ public class IntegrationTest {
      * @throws ExecutionException if some client could not start
      */
     private List<OFClient> createAndStartClient(int amountOfCLients, ScenarioHandler scenarioHandler,
-            TransportProtocol protocol) throws ExecutionException {
+            TransportProtocol protocol, ClientType clientType) throws ExecutionException {
         List<OFClient> clientsHorde = new ArrayList<>();
         for (int i = 0; i < amountOfCLients; i++) {
             LOGGER.debug("startup address in createclient: " + startupAddress.getHostAddress());
             OFClient sc = null;
-            if (protocol.equals(TransportProtocol.TCP)) {
-                sc = new SimpleClient(startupAddress.getHostAddress(), port);
+            if (clientType == ClientType.SIMPLE) {
+                if (protocol.equals(TransportProtocol.TCP)) {
+                    sc = new SimpleClient(startupAddress.getHostAddress(), port);
+                    sc.setSecuredClient(false);
+                } else if (protocol.equals(TransportProtocol.TLS)) {
+                    sc = new SimpleClient(startupAddress.getHostAddress(), port);
+                    sc.setSecuredClient(true);
+                } else {
+                    sc = new UdpSimpleClient(startupAddress.getHostAddress(), port);
+                }
+            } else if (clientType == ClientType.LISTENING) {
+                sc = new ListeningSimpleClient(0);
+                sc.setScenarioHandler(scenarioHandler);
                 sc.setSecuredClient(false);
-            } else if (protocol.equals(TransportProtocol.TLS)) {
-                sc = new SimpleClient(startupAddress.getHostAddress(), port);
-                sc.setSecuredClient(true);
             } else {
-                sc = new UdpSimpleClient(startupAddress.getHostAddress(), port);
+                LOGGER.error("Unknown type of client.");
+                throw new IllegalStateException("Unknown type of client.");
             }
+
             sc.setScenarioHandler(scenarioHandler);
             clientsHorde.add(sc);
-            sc.run();
+            //sc.run();
+            t = new Thread(sc);
+            t.start();
         }
         for (OFClient sc : clientsHorde) {
             try {
@@ -244,4 +259,21 @@ public class IntegrationTest {
         return clientsHorde;
     }
 
+    /**
+     * @throws Exception
+     */
+    @Test
+    public void testInitiateConnection() throws Exception {
+        setUp(TransportProtocol.TCP);
+
+        Deque<ClientEvent> scenario = ScenarioFactory.createHandshakeScenario();
+        ScenarioHandler handler = new ScenarioHandler(scenario);
+        List<OFClient> clients = createAndStartClient(1, handler, TransportProtocol.TCP, ClientType.LISTENING);
+        OFClient ofClient = clients.get(0);
+        ofClient.getIsOnlineFuture().get(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS);
+        int listeningClientPort = ((ListeningSimpleClient) ofClient).getPort();
+        mockPlugin.initiateConnection(switchConnectionProvider, "localhost", listeningClientPort);
+        ofClient.getScenarioDone().get();
+        LOGGER.debug("testInitiateConnection() Finished") ;
+    }
 }
index f8298bbf966c12d16c552966796dd3ffd67987e2..e83bac83b4a1c78e5bd589df4bd2b645ca977822 100644 (file)
@@ -18,6 +18,8 @@ import java.util.concurrent.TimeoutException;
 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionReadyListener;
 import org.opendaylight.openflowjava.protocol.api.connection.SwitchConnectionHandler;
+import org.opendaylight.openflowjava.protocol.impl.core.SwitchConnectionProviderImpl;
+import org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProvider;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoReplyInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoReplyInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoRequestMessage;
@@ -231,5 +233,14 @@ public class MockPlugin implements OpenflowProtocolListener, SwitchConnectionHan
         LOGGER.trace("MockPlugin().onConnectionReady()");
     }
 
-
+    /**
+     * Initiates connection to device
+     * @param switchConnectionProvider
+     * @param host - host IP
+     * @param port - port number 
+     */
+    public void initiateConnection(SwitchConnectionProviderImpl switchConnectionProvider, String host, int port) {
+        LOGGER.trace("MockPlugin().initiateConnection()");
+        switchConnectionProvider.initiateConnection(host, port);
+    }
 }
index b57500b478f34de3bc58b401b86b300b540f5294..c6910b145efc1f5d9f82ffa48f984bd92ea843fa 100644 (file)
@@ -46,5 +46,4 @@ public interface SwitchConnectionProvider extends AutoCloseable,
      * @param switchConHandler instance being informed when new switch connects
      */
     void setSwitchConnectionHandler(SwitchConnectionHandler switchConHandler);
-
 }
\ No newline at end of file
diff --git a/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/ListeningSimpleClient.java b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/ListeningSimpleClient.java
new file mode 100644 (file)
index 0000000..0288745
--- /dev/null
@@ -0,0 +1,124 @@
+package org.opendaylight.openflowjava.protocol.impl.clients;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.util.concurrent.Future;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.ExecutionException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * Listening client for testing purposes
+ * @author martin.uhlir
+ *
+ */
+public class ListeningSimpleClient implements OFClient {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ListeningSimpleClient.class);
+    private int port;
+    private boolean securedClient = false;
+    private EventLoopGroup workerGroup;
+    private SettableFuture<Boolean> isOnlineFuture;
+    private SettableFuture<Boolean> scenarioDone;
+    private ScenarioHandler scenarioHandler;
+
+    /**
+     * Constructor of the class
+     *
+     * @param host address of host
+     * @param port host listening port
+     */
+    public ListeningSimpleClient(int port) {
+        this.port = port;
+        init();
+    }
+
+    private void init() {
+        isOnlineFuture = SettableFuture.create();
+        scenarioDone = SettableFuture.create();
+    }
+
+    /**
+     * Starting class of {@link ListeningSimpleClient}
+     */
+    @Override
+    public void run() {
+        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+        workerGroup = new NioEventLoopGroup();
+        SimpleClientInitializer clientInitializer = new SimpleClientInitializer(isOnlineFuture, securedClient);
+        clientInitializer.setScenario(scenarioHandler);
+        try {
+            ServerBootstrap b = new ServerBootstrap();
+            b.group(bossGroup, workerGroup)
+                .channel(NioServerSocketChannel.class)
+                .childHandler(clientInitializer);
+
+            ChannelFuture f = b.bind(port).sync();
+            // Update port, as it may have been specified as 0
+            this.port = ((InetSocketAddress) f.channel().localAddress()).getPort();
+            isOnlineFuture.set(true);
+
+            synchronized (scenarioHandler) {
+                LOGGER.debug("WAITING FOR SCENARIO");
+                while (! scenarioHandler.isScenarioFinished()) {
+                    scenarioHandler.wait();
+                }
+            }
+        } catch (Exception ex) {
+            LOGGER.error(ex.getMessage(), ex);
+        } finally {
+            LOGGER.debug("listening client shutting down");
+            try {
+                workerGroup.shutdownGracefully().get();
+                bossGroup.shutdownGracefully().get();
+                LOGGER.debug("listening client shutdown succesful");
+            } catch (InterruptedException | ExecutionException e) {
+                LOGGER.error(e.getMessage(), e);
+            }
+        }
+        scenarioDone.set(true);
+    }
+
+    /**
+     * @return close future
+     */
+    public Future<?> disconnect() {
+        LOGGER.debug("disconnecting client");
+        return workerGroup.shutdownGracefully();
+    }
+
+    @Override
+    public void setSecuredClient(boolean securedClient) {
+        this.securedClient = securedClient;
+    }
+
+    @Override
+    public SettableFuture<Boolean> getIsOnlineFuture() {
+        return isOnlineFuture;
+    }
+
+    @Override
+    public SettableFuture<Boolean> getScenarioDone() {
+        return scenarioDone;
+    }
+
+    @Override
+    public void setScenarioHandler(ScenarioHandler scenario) {
+        this.scenarioHandler = scenario;
+    }
+
+    /**
+     * @return actual port number
+     */
+    public int getPort() {
+        return this.port;
+    }
+}
\ No newline at end of file