You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2018/04/19 06:47:13 UTC

[2/2] mina-sshd git commit: [SSHD-817] Netty nio provider

[SSHD-817] Netty nio provider


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/598c991f
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/598c991f
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/598c991f

Branch: refs/heads/master
Commit: 598c991fe4cc609c43f972fe025775fcf734933b
Parents: 5c1c8a9
Author: Guillaume Nodet <gn...@apache.org>
Authored: Tue Apr 17 22:16:55 2018 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Thu Apr 19 08:43:30 2018 +0200

----------------------------------------------------------------------
 pom.xml                                         |   1 +
 sshd-netty/pom.xml                              | 214 +++++++++++++++++++
 .../org/apache/sshd/netty/NettyIoAcceptor.java  | 174 +++++++++++++++
 .../org/apache/sshd/netty/NettyIoConnector.java | 125 +++++++++++
 .../org/apache/sshd/netty/NettyIoService.java   |  55 +++++
 .../sshd/netty/NettyIoServiceFactory.java       |  75 +++++++
 .../netty/NettyIoServiceFactoryFactory.java     |  49 +++++
 .../org/apache/sshd/netty/NettyIoSession.java   | 211 ++++++++++++++++++
 .../org/apache/sshd/netty/NettySupport.java     |  45 ++++
 ...pache.sshd.common.io.IoServiceFactoryFactory |  20 ++
 10 files changed, 969 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/598c991f/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index e3f9353..1d75f92 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1007,6 +1007,7 @@
         <module>sshd-core</module>
         <module>sshd-sftp</module>
         <module>sshd-mina</module>
+        <module>sshd-netty</module>
         <module>sshd-ldap</module>
         <module>sshd-git</module>
         <module>sshd-contrib</module>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/598c991f/sshd-netty/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-netty/pom.xml b/sshd-netty/pom.xml
new file mode 100644
index 0000000..fdb0f3e
--- /dev/null
+++ b/sshd-netty/pom.xml
@@ -0,0 +1,214 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You 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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.sshd</groupId>
+        <artifactId>sshd</artifactId>
+        <version>1.7.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+
+    <artifactId>sshd-netty</artifactId>
+    <name>Apache Mina SSHD :: Netty</name>
+    <packaging>jar</packaging>
+    <inceptionYear>2008</inceptionYear>
+
+    <properties>
+        <projectRoot>${project.basedir}/..</projectRoot>
+        <netty.version>4.1.1.Final</netty.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-transport</artifactId>
+            <version>${netty.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-handler</artifactId>
+            <version>${netty.version}</version>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-core</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-sftp</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.i2p.crypto</groupId>
+            <artifactId>eddsa</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jsch</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jzlib</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.ethz.ganymed</groupId>
+            <artifactId>ganymed-ssh2</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.not-yet-commons-ssl</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <testSourceDirectory>${build.directory}/test-sources</testSourceDirectory>
+        <testResources>
+            <testResource>
+                <directory>${build.directory}/test-resources</directory>
+            </testResource>
+        </testResources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-test-resources</id>
+                        <phase>generate-test-resources</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${build.directory}/test-resources</outputDirectory>
+                            <resources>
+                                <resource>
+                                    <directory>${projectRoot}/sshd-core/src/test/resources</directory>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>copy-test-sources</id>
+                        <phase>generate-test-sources</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${build.directory}/test-sources</outputDirectory>
+                            <resources>
+                                <resource>
+                                    <directory>${projectRoot}/sshd-core/src/test/java</directory>
+                                    <excludes>
+                                        <exclude>**/ProxyTest.java</exclude>
+                                        <exclude>**/PortForwardingTest.java</exclude>
+                                        <exclude>**/PortForwardingLoadTest.java</exclude>
+                                    </excludes>
+                                </resource>
+                                <resource>
+                                    <directory>${projectRoot}/sshd-sftp/src/test/java</directory>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                    <reportsDirectory>${project.build.directory}/surefire-reports-netty</reportsDirectory>
+                    <systemProperties>
+                        <org.apache.sshd.common.io.IoServiceFactoryFactory>org.apache.sshd.netty.NettyIoServiceFactoryFactory</org.apache.sshd.common.io.IoServiceFactoryFactory>
+                    </systemProperties>
+                    <excludes>
+                            <!-- These tests use NIO explicitly -->
+                        <exclude>**/*LoadTest.java</exclude>
+                        <exclude>**/ProxyTest.java</exclude>
+                        <exclude>**/Nio2ServiceTest.java</exclude>
+                            <!-- TODO need some more research as to why this fails on MINA but not on NIO2 -->
+                        <exclude>**/ClientDeadlockTest.java</exclude>
+                        <exclude>**/ApacheServer*Test.java</exclude>
+                        <exclude>**/ClientTest.java</exclude>
+                        <exclude>**/SpaceAvailableExtensionImplTest.java</exclude>
+                    </excludes>
+                        <!-- No need to re-run core tests that do not involve session creation -->
+                    <excludedGroups>org.apache.sshd.util.test.NoIoTestCase</excludedGroups>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <additionalparam>-Xdoclint:none</additionalparam>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/598c991f/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoAcceptor.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoAcceptor.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoAcceptor.java
new file mode 100644
index 0000000..8d88cdf
--- /dev/null
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoAcceptor.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.netty;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.io.IoAcceptor;
+import org.apache.sshd.common.io.IoHandler;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+import io.netty.util.concurrent.GlobalEventExecutor;
+
+/**
+ * The Netty based IoAcceptor implementation.
+ *
+ * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NettyIoAcceptor extends NettyIoService implements IoAcceptor {
+
+    protected final ServerBootstrap bootstrap = new ServerBootstrap();
+    protected final DefaultCloseFuture closeFuture = new DefaultCloseFuture(toString(), lock);
+    protected final Map<SocketAddress, Channel> boundAddresses = new HashMap<>();
+    protected final IoHandler handler;
+
+    public NettyIoAcceptor(NettyIoServiceFactory factory, IoHandler handler) {
+        this.factory = factory;
+        this.handler = handler;
+        channelGroup = new DefaultChannelGroup("sshd-acceptor-channels", GlobalEventExecutor.INSTANCE);
+        bootstrap.group(factory.eventLoopGroup)
+                .channel(NioServerSocketChannel.class)
+                .option(ChannelOption.SO_BACKLOG, 100)
+                .handler(new LoggingHandler(LogLevel.INFO))
+                .childHandler(new ChannelInitializer<SocketChannel>() {
+                    @Override
+                    public void initChannel(SocketChannel ch) throws Exception {
+                        ChannelPipeline p = ch.pipeline();
+                        p.addLast(new NettyIoSession(NettyIoAcceptor.this, handler).adapter);
+                    }
+                });
+    }
+
+    @Override
+    public void bind(Collection<? extends SocketAddress> addresses) throws IOException {
+        for (SocketAddress address : addresses) {
+            bind(address);
+        }
+    }
+
+    @Override
+    public void bind(SocketAddress address) throws IOException {
+        InetSocketAddress inetAddress = (InetSocketAddress) address;
+        ChannelFuture f = bootstrap.bind(inetAddress);
+        Channel channel = f.channel();
+        channelGroup.add(channel);
+        try {
+            f.sync();
+            SocketAddress bound = channel.localAddress();
+            boundAddresses.put(bound, channel);
+            channel.closeFuture().addListener(fut -> boundAddresses.remove(bound));
+        } catch (InterruptedException e) {
+            throw (InterruptedIOException) new InterruptedIOException().initCause(e);
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+    }
+
+    @Override
+    public void unbind(Collection<? extends SocketAddress> addresses) {
+        CountDownLatch latch = new CountDownLatch(addresses.size());
+        for (SocketAddress address : addresses) {
+            Channel channel = boundAddresses.get(address);
+            if (channel != null) {
+                ChannelFuture fut;
+                if (channel.isOpen()) {
+                    fut = channel.close();
+                } else {
+                    fut = channel.closeFuture();
+                }
+                fut.addListener(f -> latch.countDown());
+            } else {
+                latch.countDown();
+            }
+        }
+        try {
+            latch.await();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    @Override
+    public void unbind(SocketAddress address) {
+        Channel channel = boundAddresses.get(address);
+        if (channel != null) {
+            ChannelFuture fut;
+            if (channel.isOpen()) {
+                fut = channel.close();
+            } else {
+                fut = channel.closeFuture();
+            }
+            try {
+                fut.sync();
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    @Override
+    public void unbind() {
+        Collection<SocketAddress> addresses = getBoundAddresses();
+        if (log.isDebugEnabled()) {
+            log.debug("Unbinding {}", addresses);
+        }
+
+        unbind(addresses);
+    }
+
+    @Override
+    public Set<SocketAddress> getBoundAddresses() {
+        return new HashSet<>(boundAddresses.keySet());
+    }
+
+    @Override
+    protected CloseFuture doCloseGracefully() {
+        channelGroup.close().addListener(fut -> closeFuture.setClosed());
+        return closeFuture;
+    }
+
+    @Override
+    protected void doCloseImmediately() {
+        doCloseGracefully();
+        super.doCloseImmediately();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/598c991f/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
new file mode 100644
index 0000000..ebced0d
--- /dev/null
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.netty;
+
+import java.net.SocketAddress;
+
+import org.apache.sshd.common.future.DefaultSshFuture;
+import org.apache.sshd.common.io.IoConnectFuture;
+import org.apache.sshd.common.io.IoConnector;
+import org.apache.sshd.common.io.IoHandler;
+import org.apache.sshd.common.io.IoSession;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+import io.netty.util.concurrent.GlobalEventExecutor;
+
+/**
+ * The Netty based IoConnector implementation.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NettyIoConnector extends NettyIoService implements IoConnector {
+
+    protected final Bootstrap bootstrap = new Bootstrap();
+    protected final IoHandler handler;
+
+    public NettyIoConnector(NettyIoServiceFactory factory, IoHandler handler) {
+        this.factory = factory;
+        this.handler = handler;
+        channelGroup = new DefaultChannelGroup("sshd-connector-channels", GlobalEventExecutor.INSTANCE);
+        bootstrap.group(factory.eventLoopGroup)
+                .channel(NioSocketChannel.class)
+                .option(ChannelOption.SO_BACKLOG, 100)
+                .handler(new ChannelInitializer<SocketChannel>() {
+                    @Override
+                    protected void initChannel(SocketChannel ch) throws Exception {
+                        NettyIoSession session = new NettyIoSession(NettyIoConnector.this, handler);
+                        ChannelPipeline p = ch.pipeline();
+                        p.addLast(new LoggingHandler(LogLevel.INFO));
+                        p.addLast(session.adapter);
+                    }
+                });
+    }
+
+    @Override
+    public IoConnectFuture connect(SocketAddress address) {
+        boolean debugEnabled = log.isDebugEnabled();
+        if (debugEnabled) {
+            log.debug("Connecting to {}", address);
+        }
+
+        IoConnectFuture future = new DefaultIoConnectFuture(address, null);
+        ChannelFuture chf = bootstrap.connect(address);
+        Channel channel = chf.channel();
+        channel.attr(CONNECT_FUTURE_KEY).set(future);
+        chf.addListener(cf -> {
+            Throwable t = chf.cause();
+            if (t != null) {
+                future.setException(t);
+            } else if (chf.isCancelled()) {
+                future.cancel();
+            }
+        });
+        return future;
+    }
+
+    public static class DefaultIoConnectFuture extends DefaultSshFuture<IoConnectFuture> implements IoConnectFuture {
+        public DefaultIoConnectFuture(Object id, Object lock) {
+            super(id, lock);
+        }
+
+        @Override
+        public IoSession getSession() {
+            Object v = getValue();
+            return v instanceof IoSession ? (IoSession) v : null;
+        }
+
+        @Override
+        public Throwable getException() {
+            Object v = getValue();
+            return v instanceof Throwable ? (Throwable) v : null;
+        }
+
+        @Override
+        public boolean isConnected() {
+            return getValue() instanceof IoSession;
+        }
+
+        @Override
+        public void setSession(IoSession session) {
+            setValue(session);
+        }
+
+        @Override
+        public void setException(Throwable exception) {
+            setValue(exception);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/598c991f/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
new file mode 100644
index 0000000..9bd3ca3
--- /dev/null
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.netty;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.sshd.common.io.IoConnectFuture;
+import org.apache.sshd.common.io.IoService;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.util.closeable.AbstractCloseable;
+
+import io.netty.channel.group.ChannelGroup;
+import io.netty.util.AttributeKey;
+
+/**
+ * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NettyIoService extends AbstractCloseable implements IoService {
+
+    public static final AttributeKey<IoConnectFuture> CONNECT_FUTURE_KEY = AttributeKey.valueOf(IoConnectFuture.class.getName());
+
+    protected final AtomicLong sessionSeq = new AtomicLong();
+    protected final Map<Long, IoSession> sessions = new ConcurrentHashMap<>();
+    protected NettyIoServiceFactory factory;
+    protected ChannelGroup channelGroup;
+
+    public NettyIoService() {
+        super();
+    }
+
+    @Override
+    public Map<Long, IoSession> getManagedSessions() {
+        return sessions;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/598c991f/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoServiceFactory.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoServiceFactory.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoServiceFactory.java
new file mode 100644
index 0000000..2bc3f97
--- /dev/null
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoServiceFactory.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.netty;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.io.IoAcceptor;
+import org.apache.sshd.common.io.IoConnector;
+import org.apache.sshd.common.io.IoHandler;
+import org.apache.sshd.common.io.IoServiceFactory;
+import org.apache.sshd.common.util.closeable.AbstractCloseable;
+
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+
+/**
+ * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NettyIoServiceFactory extends AbstractCloseable implements IoServiceFactory {
+
+    protected final EventLoopGroup eventLoopGroup;
+    protected final boolean closeEventLoopGroup;
+
+    public NettyIoServiceFactory() {
+        this(null);
+    }
+
+    public NettyIoServiceFactory(EventLoopGroup group) {
+        this.eventLoopGroup = group != null ? group : new NioEventLoopGroup();
+        this.closeEventLoopGroup = group == null;
+    }
+
+    @Override
+    public IoConnector createConnector(IoHandler handler) {
+        return new NettyIoConnector(this, handler);
+    }
+
+    @Override
+    public IoAcceptor createAcceptor(IoHandler handler) {
+        return new NettyIoAcceptor(this, handler);
+    }
+
+    @Override
+    protected CloseFuture doCloseGracefully() {
+        if (closeEventLoopGroup) {
+            eventLoopGroup.shutdownGracefully().addListener(fut -> closeFuture.setClosed());
+        } else {
+            closeFuture.setClosed();
+        }
+        return closeFuture;
+    }
+
+    @Override
+    protected void doCloseImmediately() {
+        doCloseGracefully();
+        super.doCloseImmediately();
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/598c991f/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoServiceFactoryFactory.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoServiceFactoryFactory.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoServiceFactoryFactory.java
new file mode 100644
index 0000000..bead4aa
--- /dev/null
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoServiceFactoryFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.netty;
+
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.io.IoServiceFactory;
+import org.apache.sshd.common.io.IoServiceFactoryFactory;
+
+import io.netty.channel.EventLoopGroup;
+
+/**
+ * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NettyIoServiceFactoryFactory implements IoServiceFactoryFactory {
+
+    protected final EventLoopGroup eventLoopGroup;
+
+    public NettyIoServiceFactoryFactory() {
+        this(null);
+    }
+
+    public NettyIoServiceFactoryFactory(EventLoopGroup eventLoopGroup) {
+        this.eventLoopGroup = eventLoopGroup;
+    }
+
+    @Override
+    public IoServiceFactory create(FactoryManager manager) {
+        return new NettyIoServiceFactory(eventLoopGroup);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/598c991f/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoSession.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoSession.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoSession.java
new file mode 100644
index 0000000..57aecc6
--- /dev/null
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoSession.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.netty;
+
+import java.net.SocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.io.AbstractIoWriteFuture;
+import org.apache.sshd.common.io.IoConnectFuture;
+import org.apache.sshd.common.io.IoHandler;
+import org.apache.sshd.common.io.IoService;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.closeable.AbstractCloseable;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+
+/**
+ * The Netty based IoSession implementation.
+ *
+ * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NettyIoSession extends AbstractCloseable implements IoSession {
+
+    protected final Map<Object, Object> attributes = new HashMap<>();
+    protected final NettyIoService service;
+    protected final IoHandler handler;
+    protected final DefaultCloseFuture closeFuture = new DefaultCloseFuture(toString(), lock);
+    protected final long id;
+    protected ChannelHandlerContext context;
+    protected SocketAddress remoteAddr;
+    protected ChannelFuture prev;
+    protected final ChannelInboundHandlerAdapter adapter = new Adapter();
+
+    public NettyIoSession(NettyIoService service, IoHandler handler) {
+        this.service = service;
+        this.handler = handler;
+        this.id = service.sessionSeq.incrementAndGet();
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    @Override
+    public Object getAttribute(Object key) {
+        return attributes.get(key);
+    }
+
+    @Override
+    public Object setAttribute(Object key, Object value) {
+        return attributes.put(key, value);
+    }
+
+    @Override
+    public Object setAttributeIfAbsent(Object key, Object value) {
+        return attributes.putIfAbsent(key, value);
+    }
+
+    @Override
+    public Object removeAttribute(Object key) {
+        return attributes.remove(key);
+    }
+
+    @Override
+    public SocketAddress getRemoteAddress() {
+        return remoteAddr;
+    }
+
+    @Override
+    public SocketAddress getLocalAddress() {
+        return context.channel().localAddress();
+    }
+
+    @Override
+    public IoWriteFuture writePacket(Buffer buffer) {
+        ByteBuf buf = Unpooled.buffer(buffer.available());
+        buf.writeBytes(buffer.array(), buffer.rpos(), buffer.available());
+        DefaultIoWriteFuture msg = new DefaultIoWriteFuture(getRemoteAddress(), null);
+        ChannelPromise next = context.newPromise();
+        prev.addListener(whatever -> {
+            if (context != null) {
+                context.writeAndFlush(buf, next);
+            }
+        });
+        prev = next;
+        next.addListener(fut -> {
+            if (fut.isSuccess()) {
+                msg.setValue(Boolean.TRUE);
+            } else {
+                msg.setValue(fut.cause());
+            }
+        });
+        return msg;
+    }
+
+    @Override
+    public IoService getService() {
+        return service;
+    }
+
+    @Override
+    protected CloseFuture doCloseGracefully() {
+        context.writeAndFlush(Unpooled.EMPTY_BUFFER).
+                addListener(ChannelFutureListener.CLOSE).
+                addListener(fut -> {
+                    closeFuture.setClosed();
+                });
+        return closeFuture;
+    }
+
+    @Override
+    protected void doCloseImmediately() {
+        context.close();
+        super.doCloseImmediately();
+    }
+
+    protected void channelActive(ChannelHandlerContext ctx) throws Exception {
+        context = ctx;
+        service.channelGroup.add(ctx.channel());
+        service.sessions.put(id, NettyIoSession.this);
+        prev = context.newPromise().setSuccess();
+        remoteAddr = context.channel().remoteAddress();
+        handler.sessionCreated(NettyIoSession.this);
+        IoConnectFuture future = ctx.channel().attr(NettyIoService.CONNECT_FUTURE_KEY).get();
+        if (future != null) {
+            future.setSession(NettyIoSession.this);
+        }
+    }
+
+    protected void channelInactive(ChannelHandlerContext ctx) throws Exception {
+        service.sessions.remove(id);
+        handler.sessionClosed(NettyIoSession.this);
+        context = null;
+    }
+
+    protected void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        ByteBuf buf = (ByteBuf) msg;
+        handler.messageReceived(NettyIoSession.this, NettySupport.asReadable(buf));
+    }
+
+    protected void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        handler.exceptionCaught(NettyIoSession.this, cause);
+    }
+
+    protected static class DefaultIoWriteFuture extends AbstractIoWriteFuture {
+
+        public DefaultIoWriteFuture(Object id, Object lock) {
+            super(id, lock);
+        }
+    }
+
+    /**
+     * Simple netty adapter to use as a bridge.
+     */
+    protected class Adapter extends ChannelInboundHandlerAdapter {
+
+        public Adapter() {
+            super();
+        }
+
+        @Override
+        public void channelActive(ChannelHandlerContext ctx) throws Exception {
+            NettyIoSession.this.channelActive(ctx);
+        }
+
+        @Override
+        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+            NettyIoSession.this.channelInactive(ctx);
+        }
+
+        @Override
+        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+            NettyIoSession.this.channelRead(ctx, msg);
+        }
+
+        @Override
+        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+            NettyIoSession.this.exceptionCaught(ctx, cause);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/598c991f/sshd-netty/src/main/java/org/apache/sshd/netty/NettySupport.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettySupport.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettySupport.java
new file mode 100644
index 0000000..9f9de89
--- /dev/null
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettySupport.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.netty;
+
+import org.apache.sshd.common.util.Readable;
+
+import io.netty.buffer.ByteBuf;
+
+public final class NettySupport {
+
+    private NettySupport() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    public static Readable asReadable(final ByteBuf buffer) {
+        return new Readable() {
+            @Override
+            public int available() {
+                return buffer.readableBytes();
+            }
+
+            @Override
+            public void getRawBytes(byte[] data, int offset, int len) {
+                buffer.getBytes(0, data, offset, len);
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/598c991f/sshd-netty/src/main/resources/META-INF/services/org.apache.sshd.common.io.IoServiceFactoryFactory
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/resources/META-INF/services/org.apache.sshd.common.io.IoServiceFactoryFactory b/sshd-netty/src/main/resources/META-INF/services/org.apache.sshd.common.io.IoServiceFactoryFactory
new file mode 100644
index 0000000..be71649
--- /dev/null
+++ b/sshd-netty/src/main/resources/META-INF/services/org.apache.sshd.common.io.IoServiceFactoryFactory
@@ -0,0 +1,20 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  The ASF licenses this file
+## to you 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.
+##
+
+org.apache.sshd.netty.NettyIoServiceFactoryFactory