http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
deleted file mode 100644
index d059d36..0000000
--- 
a/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.common.subsystem.sftp;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SftpConstantsTest extends BaseTestSupport {
-    public SftpConstantsTest() {
-        super();
-    }
-
-    @Test
-    public void testRenameModesNotMarkedAsOpcodes() {
-        for (int cmd : new int[]{
-            SftpConstants.SSH_FXP_RENAME_OVERWRITE,
-            SftpConstants.SSH_FXP_RENAME_ATOMIC,
-            SftpConstants.SSH_FXP_RENAME_NATIVE
-        }) {
-            String name = SftpConstants.getCommandMessageName(cmd);
-            assertFalse("Mismatched name for " + cmd + ": " + name, 
name.startsWith("SSH_FXP_RENAME_"));
-        }
-    }
-
-    @Test
-    public void testRealPathModesNotMarkedAsOpcodes() {
-        for (int cmd = SftpConstants.SSH_FXP_REALPATH_NO_CHECK; cmd <= 
SftpConstants.SSH_FXP_REALPATH_STAT_IF; cmd++) {
-            String name = SftpConstants.getCommandMessageName(cmd);
-            assertFalse("Mismatched name for " + cmd + ": " + name, 
name.startsWith("SSH_FXP_REALPATH_"));
-        }
-    }
-
-    @Test
-    public void testSubstatusNameResolution() {
-        for (int status = SftpConstants.SSH_FX_OK; status <= 
SftpConstants.SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK; status++) {
-            String name = SftpConstants.getStatusName(status);
-            assertTrue("Failed to convert status=" + status + ": " + name, 
name.startsWith("SSH_FX_"));
-        }
-    }
-
-    @Test
-    public void testSubstatusMessageResolution() {
-        for (int status = SftpConstants.SSH_FX_OK; status <= 
SftpConstants.SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK; status++) {
-            String message = SftpHelper.resolveStatusMessage(status);
-            assertTrue("Missing message for status=" + status, 
GenericUtils.isNotEmpty(message));
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
deleted file mode 100644
index 704aa05..0000000
--- 
a/sshd-core/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.common.subsystem.sftp;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SftpUniversalOwnerAndGroupTest extends BaseTestSupport {
-    public SftpUniversalOwnerAndGroupTest() {
-        super();
-    }
-
-    @Test
-    public void testNameFormat() {
-        for (SftpUniversalOwnerAndGroup value : 
SftpUniversalOwnerAndGroup.VALUES) {
-            String name = value.getName();
-            assertFalse(value.name() + ": empty name", 
GenericUtils.isEmpty(name));
-            assertTrue(value.name() + ": bad suffix", 
name.charAt(name.length() - 1) == '@');
-
-            for (int index = 0; index < name.length() - 1; index++) {
-                char ch = name.charAt(index);
-                if ((ch < 'A') || (ch > 'Z')) {
-                    fail("Non-uppercase character in " + name);
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testFromName() {
-        for (String name : new String[]{null, "", getCurrentTestName()}) {
-            assertNull("Unexpected value for '" + name + "'", 
SftpUniversalOwnerAndGroup.fromName(name));
-        }
-
-        for (SftpUniversalOwnerAndGroup expected : 
SftpUniversalOwnerAndGroup.VALUES) {
-            String name = expected.getName();
-            for (int index = 0; index < name.length(); index++) {
-                assertSame(name, expected, 
SftpUniversalOwnerAndGroup.fromName(name));
-                name = shuffleCase(name);
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java 
b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
index 2f07cab..8c083a3 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
@@ -55,7 +55,6 @@ import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.auth.UserAuthMethodFactory;
 import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.channel.ChannelListener;
-import org.apache.sshd.common.channel.TestChannelListener;
 import org.apache.sshd.common.channel.Window;
 import org.apache.sshd.common.channel.WindowClosedException;
 import org.apache.sshd.common.io.IoSession;
@@ -78,6 +77,7 @@ import org.apache.sshd.server.session.ServerSessionImpl;
 import org.apache.sshd.util.test.BaseTestSupport;
 import org.apache.sshd.util.test.EchoShell;
 import org.apache.sshd.util.test.EchoShellFactory;
+import org.apache.sshd.util.test.TestChannelListener;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.FixMethodOrder;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
deleted file mode 100644
index 6420411..0000000
--- 
a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.server.subsystem.sftp;
-
-import java.util.concurrent.ExecutorService;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SftpSubsystemFactoryTest extends BaseTestSupport {
-    public SftpSubsystemFactoryTest() {
-        super();
-    }
-
-    /**
-     * Make sure that the builder returns a factory with the default values
-     * if no {@code withXXX} method is invoked
-     */
-    @Test
-    public void testBuilderDefaultFactoryValues() {
-        SftpSubsystemFactory factory = new 
SftpSubsystemFactory.Builder().build();
-        assertNull("Mismatched executor", factory.getExecutorService());
-        assertFalse("Mismatched shutdown state", factory.isShutdownOnExit());
-        assertSame("Mismatched unsupported attribute policy", 
SftpSubsystemFactory.DEFAULT_POLICY, factory.getUnsupportedAttributePolicy());
-    }
-
-    /**
-     * Make sure that the builder initializes correctly the built factory
-     */
-    @Test
-    public void testBuilderCorrectlyInitializesFactory() {
-        SftpSubsystemFactory.Builder builder = new 
SftpSubsystemFactory.Builder();
-        ExecutorService service = dummyExecutor();
-        SftpSubsystemFactory factory = builder.withExecutorService(service)
-                .withShutdownOnExit(true)
-                .build();
-        assertSame("Mismatched executor", service, 
factory.getExecutorService());
-        assertTrue("Mismatched shutdown state", factory.isShutdownOnExit());
-
-        for (UnsupportedAttributePolicy policy : 
UnsupportedAttributePolicy.VALUES) {
-            SftpSubsystemFactory actual = 
builder.withUnsupportedAttributePolicy(policy).build();
-            assertSame("Mismatched unsupported attribute policy", policy, 
actual.getUnsupportedAttributePolicy());
-        }
-    }
-
-    /**
-     * <UL>
-     * <LI>
-     * Make sure the builder returns new instances on every call to
-     * {@link SftpSubsystemFactory.Builder#build()} method
-     * </LI>
-     *
-     * <LI>
-     * Make sure values are preserved between successive invocations
-     * of the {@link SftpSubsystemFactory.Builder#build()} method
-     * </LI>
-     * </UL
-     */
-    @Test
-    public void testBuilderUniqueInstance() {
-        SftpSubsystemFactory.Builder builder = new 
SftpSubsystemFactory.Builder();
-        SftpSubsystemFactory f1 = 
builder.withExecutorService(dummyExecutor()).build();
-        SftpSubsystemFactory f2 = builder.build();
-        assertNotSame("No new instance built", f1, f2);
-        assertSame("Mismatched executors", f1.getExecutorService(), 
f2.getExecutorService());
-
-        SftpSubsystemFactory f3 = 
builder.withExecutorService(dummyExecutor()).build();
-        assertNotSame("Executor service not changed", f1.getExecutorService(), 
f3.getExecutorService());
-    }
-
-    private static ExecutorService dummyExecutor() {
-        return Mockito.mock(ExecutorService.class);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
 
b/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
deleted file mode 100644
index e6b10e0..0000000
--- 
a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * 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.server.subsystem.sftp;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.TreeMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.config.SshConfigFileReader;
-import org.apache.sshd.common.io.BuiltinIoServiceFactoryFactories;
-import org.apache.sshd.common.io.IoServiceFactory;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-import org.apache.sshd.server.Command;
-import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.server.Environment;
-import org.apache.sshd.server.ExitCallback;
-import org.apache.sshd.server.SessionAware;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.auth.password.AcceptAllPasswordAuthenticator;
-import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
-import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
-import org.apache.sshd.server.scp.ScpCommandFactory;
-import org.apache.sshd.server.session.ServerSession;
-import org.apache.sshd.server.shell.InteractiveProcessShellFactory;
-import org.apache.sshd.util.test.Utils;
-
-/**
- * A basic implementation to allow remote mounting of the local file system 
via SFTP
- *
- * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
- */
-public final class SshFsMounter {
-    public static class MounterCommand extends AbstractLoggingBean implements 
Command, SessionAware, Runnable {
-        private final String command;
-        private final String cmdName;
-        private final List<String> args;
-        private String username;
-        private InputStream stdin;
-        private PrintStream stdout;
-        private PrintStream stderr;
-        private ExitCallback callback;
-        private ExecutorService executor;
-        private Future<?> future;
-
-        public MounterCommand(String command) {
-            this.command = ValidateUtils.checkNotNullAndNotEmpty(command, "No 
command");
-
-            String[] comps = GenericUtils.split(this.command, ' ');
-            int numComps = GenericUtils.length(comps);
-            cmdName = 
GenericUtils.trimToEmpty(ValidateUtils.checkNotNullAndNotEmpty(comps[0], "No 
command name"));
-            if (numComps > 1) {
-                args = new ArrayList<>(numComps - 1);
-                for (int index = 1; index < numComps; index++) {
-                    String c = GenericUtils.trimToEmpty(comps[index]);
-                    if (GenericUtils.isEmpty(c)) {
-                        continue;
-                    }
-
-                    args.add(c);
-                }
-            } else {
-                args = Collections.emptyList();
-            }
-
-            log.info("<init>(" + command + ")");
-        }
-
-        @Override
-        public void run() {
-            try {
-                log.info("run(" + username + ")[" + command + "] start");
-                if ("id".equals(cmdName)) {
-                    int numArgs = GenericUtils.size(args);
-                    if (numArgs <= 0) {
-                        stdout.println("uid=0(root) gid=0(root) 
groups=0(root)");
-                    } else if (numArgs == 1) {
-                        String modifier = args.get(0);
-                        if ("-u".equals(modifier) || "-G".equals(modifier)) {
-                            stdout.println("0");
-                        } else {
-                            throw new IllegalArgumentException("Unknown 
modifier: " + modifier);
-                        }
-                    } else {
-                        throw new IllegalArgumentException("Unexpected extra 
command arguments");
-                    }
-                } else {
-                    throw new UnsupportedOperationException("Unknown command");
-                }
-
-                log.info("run(" + username + ")[" + command + "] end");
-                callback.onExit(0);
-            } catch (Exception e) {
-                log.error("run(" + username + ")[" + command + "] " + 
e.getClass().getSimpleName() + ": " + e.getMessage(), e);
-                stderr.append(e.getClass().getSimpleName()).append(": 
").println(e.getMessage());
-                callback.onExit(-1, e.toString());
-            }
-        }
-
-        @Override
-        public void setSession(ServerSession session) {
-            username = session.getUsername();
-        }
-
-        @Override
-        public void setInputStream(InputStream in) {
-            this.stdin = in;
-        }
-
-        @Override
-        public void setOutputStream(OutputStream out) {
-            this.stdout = new PrintStream(out, true);
-        }
-
-        @Override
-        public void setErrorStream(OutputStream err) {
-            this.stderr = new PrintStream(err, true);
-        }
-
-        @Override
-        public void setExitCallback(ExitCallback callback) {
-            this.callback = callback;
-        }
-
-        @Override
-        public void start(Environment env) throws IOException {
-            executor = 
ThreadUtils.newSingleThreadExecutor(getClass().getSimpleName());
-            future = executor.submit(this);
-        }
-
-        @Override
-        public void destroy() {
-            stopCommand();
-
-            if (stdout != null) {
-                try {
-                    log.info("destroy(" + username + ")[" + command + "] close 
stdout");
-                    stdout.close();
-                    log.info("destroy(" + username + ")[" + command + "] 
stdout closed");
-                } finally {
-                    stdout = null;
-                }
-            }
-
-            if (stderr != null) {
-                try {
-                    log.info("destroy(" + username + ")[" + command + "] close 
stderr");
-                    stderr.close();
-                    log.info("destroy(" + username + ")[" + command + "] 
stderr closed");
-                } finally {
-                    stderr = null;
-                }
-            }
-
-            if (stdin != null) {
-                try {
-                    log.info("destroy(" + username + ")[" + command + "] close 
stdin");
-                    stdin.close();
-                    log.info("destroy(" + username + ")[" + command + "] stdin 
closed");
-                } catch (IOException e) {
-                    log.warn("destroy(" + username + ")[" + command + "] 
failed (" + e.getClass().getSimpleName() + ") to close stdin: " + 
e.getMessage());
-                    if (log.isDebugEnabled()) {
-                        log.debug("destroy(" + username + ")[" + command + "] 
failure details", e);
-                    }
-                } finally {
-                    stdin = null;
-                }
-            }
-        }
-
-        private void stopCommand() {
-            if ((future != null) && (!future.isDone())) {
-                try {
-                    log.info("stopCommand(" + username + ")[" + command + "] 
cancelling");
-                    future.cancel(true);
-                    log.info("stopCommand(" + username + ")[" + command + "] 
cancelled");
-                } finally {
-                    future = null;
-                }
-            }
-
-            if ((executor != null) && (!executor.isShutdown())) {
-                try {
-                    log.info("stopCommand(" + username + ")[" + command + "] 
shutdown executor");
-                    executor.shutdownNow();
-                    log.info("stopCommand(" + username + ")[" + command + "] 
executor shut down");
-                } finally {
-                    executor = null;
-                }
-            }
-        }
-    }
-
-    public static class MounterCommandFactory implements CommandFactory {
-        public static final MounterCommandFactory INSTANCE = new 
MounterCommandFactory();
-
-        public MounterCommandFactory() {
-            super();
-        }
-
-        @Override
-        public Command createCommand(String command) {
-            return new MounterCommand(command);
-        }
-    }
-
-    private SshFsMounter() {
-        throw new UnsupportedOperationException("No instance");
-    }
-
-    //////////////////////////////////////////////////////////////////////////
-
-    public static void main(String[] args) throws Exception {
-        int port = SshConfigFileReader.DEFAULT_PORT;
-        boolean error = false;
-        Map<String, Object> options = new 
TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        int numArgs = GenericUtils.length(args);
-        for (int i = 0; i < numArgs; i++) {
-            String argName = args[i];
-            if ("-p".equals(argName)) {
-                if ((i + 1) >= numArgs) {
-                    System.err.println("option requires an argument: " + 
argName);
-                    break;
-                }
-                port = Integer.parseInt(args[++i]);
-            } else if ("-io".equals(argName)) {
-                if (i + 1 >= numArgs) {
-                    System.err.println("option requires an argument: " + 
argName);
-                    break;
-                }
-
-                String provider = args[++i];
-                if ("mina".equals(provider)) {
-                    System.setProperty(IoServiceFactory.class.getName(), 
BuiltinIoServiceFactoryFactories.MINA.getFactoryClassName());
-                } else if ("nio2".endsWith(provider)) {
-                    System.setProperty(IoServiceFactory.class.getName(), 
BuiltinIoServiceFactoryFactories.NIO2.getFactoryClassName());
-                } else {
-                    System.err.println("provider should be mina or nio2: " + 
argName);
-                    error = true;
-                    break;
-                }
-            } else if ("-o".equals(argName)) {
-                if ((i + 1) >= numArgs) {
-                    System.err.println("option requires and argument: " + 
argName);
-                    error = true;
-                    break;
-                }
-                String opt = args[++i];
-                int idx = opt.indexOf('=');
-                if (idx <= 0) {
-                    System.err.println("bad syntax for option: " + opt);
-                    error = true;
-                    break;
-                }
-                options.put(opt.substring(0, idx), opt.substring(idx + 1));
-            } else if (argName.startsWith("-")) {
-                System.err.println("illegal option: " + argName);
-                error = true;
-                break;
-            } else {
-                System.err.println("extra argument: " + argName);
-                error = true;
-                break;
-            }
-        }
-        if (error) {
-            System.err.println("usage: sshfs [-p port] [-io mina|nio2] [-o 
option=value]");
-            System.exit(-1);
-        }
-
-        SshServer sshd = Utils.setupTestServer(SshFsMounter.class);
-        Map<String, Object> props = sshd.getProperties();
-        props.putAll(options);
-        PropertyResolver resolver = 
PropertyResolverUtils.toPropertyResolver(options);
-        File targetFolder = 
Objects.requireNonNull(Utils.detectTargetFolder(MounterCommandFactory.class), 
"Failed to detect target folder");
-        if (SecurityUtils.isBouncyCastleRegistered()) {
-            
sshd.setKeyPairProvider(SecurityUtils.createGeneratorHostKeyProvider(new 
File(targetFolder, "key.pem").toPath()));
-        } else {
-            sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new 
File(targetFolder, "key.ser")));
-        }
-        // Should come AFTER key pair provider setup so auto-welcome can be 
generated if needed
-        SshServer.setupServerBanner(sshd, resolver);
-
-        sshd.setShellFactory(InteractiveProcessShellFactory.INSTANCE);
-        sshd.setPasswordAuthenticator(AcceptAllPasswordAuthenticator.INSTANCE);
-        sshd.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
-        sshd.setCommandFactory(new 
ScpCommandFactory.Builder().withDelegate(MounterCommandFactory.INSTANCE).build());
-        sshd.setSubsystemFactories(Collections.singletonList(new 
SftpSubsystemFactory()));
-        sshd.setPort(port);
-
-        System.err.println("Starting SSHD on port " + port);
-        sshd.start();
-        Thread.sleep(Long.MAX_VALUE);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-core/src/test/java/org/apache/sshd/util/test/TestChannelListener.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/util/test/TestChannelListener.java 
b/sshd-core/src/test/java/org/apache/sshd/util/test/TestChannelListener.java
new file mode 100644
index 0000000..bc34c25
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/TestChannelListener.java
@@ -0,0 +1,155 @@
+/*
+ * 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.util.test;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.channel.ChannelListener;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+import org.junit.Assert;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class TestChannelListener extends AbstractLoggingBean implements 
ChannelListener, NamedResource {
+    private final String name;
+    private final Collection<Channel> activeChannels = new 
CopyOnWriteArraySet<>();
+    private final Semaphore activeChannelsCounter = new Semaphore(0);
+    private final Collection<Channel> openChannels = new 
CopyOnWriteArraySet<>();
+    private final Semaphore openChannelsCounter = new Semaphore(0);
+    private final Collection<Channel> failedChannels = new 
CopyOnWriteArraySet<>();
+    private final Semaphore failedChannelsCounter = new Semaphore(0);
+    private final Map<Channel, Collection<String>> channelStateHints = new 
ConcurrentHashMap<>();
+    private final Semaphore chanelStateCounter = new Semaphore(0);
+    private final Semaphore modificationsCounter = new Semaphore(0);
+    private final Semaphore closedChannelsCounter = new Semaphore(0);
+
+    public TestChannelListener(String discriminator) {
+        super(discriminator);
+        name = discriminator;
+    }
+
+    public boolean waitForModification(long timeout, TimeUnit unit) throws 
InterruptedException {
+        return modificationsCounter.tryAcquire(timeout, unit);
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public Collection<Channel> getActiveChannels() {
+        return activeChannels;
+    }
+
+    @Override
+    public void channelInitialized(Channel channel) {
+        Assert.assertTrue("Same channel instance re-initialized: " + channel, 
activeChannels.add(channel));
+        activeChannelsCounter.release();
+        modificationsCounter.release();
+        log.info("channelInitialized({})", channel);
+    }
+
+    public boolean waitForActiveChannelsChange(long timeout, TimeUnit unit) 
throws InterruptedException {
+        return activeChannelsCounter.tryAcquire(timeout, unit);
+    }
+
+    public Collection<Channel> getOpenChannels() {
+        return openChannels;
+    }
+
+    @Override
+    public void channelOpenSuccess(Channel channel) {
+        Assert.assertTrue("Open channel not activated: " + channel, 
activeChannels.contains(channel));
+        Assert.assertTrue("Same channel instance re-opened: " + channel, 
openChannels.add(channel));
+        openChannelsCounter.release();
+        modificationsCounter.release();
+        log.info("channelOpenSuccess({})", channel);
+    }
+
+    public boolean waitForOpenChannelsChange(long timeout, TimeUnit unit) 
throws InterruptedException {
+        return openChannelsCounter.tryAcquire(timeout, unit);
+    }
+
+    public Collection<Channel> getFailedChannels() {
+        return failedChannels;
+    }
+
+    @Override
+    public void channelOpenFailure(Channel channel, Throwable reason) {
+        Assert.assertTrue("Failed channel not activated: " + channel, 
activeChannels.contains(channel));
+        Assert.assertTrue("Same channel instance re-failed: " + channel, 
failedChannels.add(channel));
+        failedChannelsCounter.release();
+        modificationsCounter.release();
+        log.warn("channelOpenFailure({}) {} : {}", channel, 
reason.getClass().getSimpleName(), reason.getMessage());
+        if (log.isDebugEnabled()) {
+            log.debug("channelOpenFailure(" + channel + ") details", reason);
+        }
+    }
+
+    public boolean waitForFailedChannelsChange(long timeout, TimeUnit unit) 
throws InterruptedException {
+        return failedChannelsCounter.tryAcquire(timeout, unit);
+    }
+
+    @Override
+    public void channelClosed(Channel channel, Throwable reason) {
+        Assert.assertTrue("Unknown closed channel instance: " + channel, 
activeChannels.remove(channel));
+        activeChannelsCounter.release();
+        closedChannelsCounter.release();
+        modificationsCounter.release();
+        log.info("channelClosed({})", channel);
+    }
+
+    public boolean waitForClosedChannelsChange(long timeout, TimeUnit unit) 
throws InterruptedException {
+        return closedChannelsCounter.tryAcquire(timeout, unit);
+    }
+
+    public Map<Channel, Collection<String>> getChannelStateHints() {
+        return channelStateHints;
+    }
+
+    @Override
+    public void channelStateChanged(Channel channel, String hint) {
+        Collection<String> hints;
+        synchronized (channelStateHints) {
+            hints = channelStateHints.get(channel);
+            if (hints == null) {
+                hints = new CopyOnWriteArrayList<>();
+                channelStateHints.put(channel, hints);
+            }
+        }
+
+        hints.add(hint);
+        chanelStateCounter.release();
+        modificationsCounter.release();
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + getName() + "]";
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-git/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-git/pom.xml b/sshd-git/pom.xml
index d0f6c6a..f9e16a7 100644
--- a/sshd-git/pom.xml
+++ b/sshd-git/pom.xml
@@ -61,6 +61,12 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-sftp</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-mina/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-mina/pom.xml b/sshd-mina/pom.xml
index b14b18f..0234e85 100644
--- a/sshd-mina/pom.xml
+++ b/sshd-mina/pom.xml
@@ -50,6 +50,19 @@
 
         <!-- 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>
@@ -107,20 +120,54 @@
     </dependencies>
 
     <build>
-        
<testSourceDirectory>${projectRoot}/sshd-core/src/test/java</testSourceDirectory>
-        <resources>
-            <resource>
-                <directory>src/main/filtered-resources</directory>
-                <filtering>true</filtering>
-            </resource>
-            <resource>
-                
<directory>${projectRoot}/sshd-core/src/test/resources</directory>
-                <targetPath>${project.build.testOutputDirectory}</targetPath>
-            </resource>
-        </resources>
+        
<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>
+                                </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>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-sftp/pom.xml b/sshd-sftp/pom.xml
new file mode 100644
index 0000000..7d4f74a
--- /dev/null
+++ b/sshd-sftp/pom.xml
@@ -0,0 +1,103 @@
+<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>
+    </parent>
+
+    <artifactId>sshd-sftp</artifactId>
+    <name>Apache Mina SSHD :: SFTP</name>
+    <packaging>jar</packaging>
+    <inceptionYear>2018</inceptionYear>
+
+    <properties>
+        <projectRoot>${project.basedir}/..</projectRoot>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.sshd</groupId>
+            <artifactId>sshd-core</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <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>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>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                </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/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClient.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClient.java 
b/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClient.java
new file mode 100644
index 0000000..1034046
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClient.java
@@ -0,0 +1,179 @@
+/*
+ * 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.client.simple;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.channels.Channel;
+import java.security.KeyPair;
+import java.util.Objects;
+
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * A simplified <U>synchronous</U> API for obtaining SFTP sessions.
+ *
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public interface SimpleSftpClient extends Channel {
+    /**
+     * Creates an SFTP session on the default port and logs in using the 
provided credentials
+     *
+     * @param host The target host name or address
+     * @param username Username
+     * @param password Password
+     * @return Created {@link SftpClient} - <B>Note:</B> closing the client 
also closes its
+     * underlying session
+     * @throws IOException If failed to login or authenticate
+     */
+    default SftpClient sftpLogin(String host, String username, String 
password) throws IOException {
+        return sftpLogin(host, SimpleClientConfigurator.DEFAULT_PORT, 
username, password);
+    }
+
+    /**
+     * Creates an SFTP session using the provided credentials
+     *
+     * @param host The target host name or address
+     * @param port The target port
+     * @param username Username
+     * @param password Password
+     * @return Created {@link SftpClient} - <B>Note:</B> closing the client 
also closes its
+     * underlying session
+     * @throws IOException If failed to login or authenticate
+     */
+    default SftpClient sftpLogin(String host, int port, String username, 
String password) throws IOException {
+        return 
sftpLogin(InetAddress.getByName(ValidateUtils.checkNotNullAndNotEmpty(host, "No 
host")), port, username, password);
+    }
+
+    /**
+     * Creates an SFTP session on the default port and logs in using the 
provided credentials
+     *
+     * @param host The target host name or address
+     * @param username Username
+     * @param identity The {@link KeyPair} identity
+     * @return Created {@link SftpClient} - <B>Note:</B> closing the client 
also closes its
+     * underlying session
+     * @throws IOException If failed to login or authenticate
+     */
+    default SftpClient sftpLogin(String host, String username, KeyPair 
identity) throws IOException {
+        return sftpLogin(host, SimpleClientConfigurator.DEFAULT_PORT, 
username, identity);
+    }
+
+    /**
+     * Creates an SFTP session using the provided credentials
+     *
+     * @param host The target host name or address
+     * @param port The target port
+     * @param username Username
+     * @param identity The {@link KeyPair} identity
+     * @return Created {@link SftpClient} - <B>Note:</B> closing the client 
also closes its
+     * underlying session
+     * @throws IOException If failed to login or authenticate
+     */
+    default SftpClient sftpLogin(String host, int port, String username, 
KeyPair identity) throws IOException {
+        return 
sftpLogin(InetAddress.getByName(ValidateUtils.checkNotNullAndNotEmpty(host, "No 
host")), port, username, identity);
+    }
+
+    /**
+     * Creates an SFTP session on the default port and logs in using the 
provided credentials
+     *
+     * @param host The target host {@link InetAddress}
+     * @param username Username
+     * @param password Password
+     * @return Created {@link SftpClient} - <B>Note:</B> closing the client 
also closes its
+     * underlying session
+     * @throws IOException If failed to login or authenticate
+     */
+    default SftpClient sftpLogin(InetAddress host, String username, String 
password) throws IOException {
+        return sftpLogin(host, SimpleClientConfigurator.DEFAULT_PORT, 
username, password);
+    }
+
+    /**
+     * Creates an SFTP session using the provided credentials
+     *
+     * @param host The target host {@link InetAddress}
+     * @param port The target port
+     * @param username Username
+     * @param password Password
+     * @return Created {@link SftpClient} - <B>Note:</B> closing the client 
also closes its
+     * underlying session
+     * @throws IOException If failed to login or authenticate
+     */
+    default SftpClient sftpLogin(InetAddress host, int port, String username, 
String password) throws IOException {
+        return sftpLogin(new InetSocketAddress(Objects.requireNonNull(host, 
"No host address"), port), username, password);
+    }
+
+    /**
+     * Creates an SFTP session on the default port and logs in using the 
provided credentials
+     *
+     * @param host The target host {@link InetAddress}
+     * @param username Username
+     * @param identity The {@link KeyPair} identity
+     * @return Created {@link SftpClient} - <B>Note:</B> closing the client 
also closes its
+     * underlying session
+     * @throws IOException If failed to login or authenticate
+     */
+    default SftpClient sftpLogin(InetAddress host, String username, KeyPair 
identity) throws IOException {
+        return sftpLogin(host, SimpleClientConfigurator.DEFAULT_PORT, 
username, identity);
+    }
+
+    /**
+     * Creates an SFTP session using the provided credentials
+     *
+     * @param host The target host {@link InetAddress}
+     * @param port The target port
+     * @param username Username
+     * @param identity The {@link KeyPair} identity
+     * @return Created {@link SftpClient} - <B>Note:</B> closing the client 
also closes its
+     * underlying session
+     * @throws IOException If failed to login or authenticate
+     */
+    default SftpClient sftpLogin(InetAddress host, int port, String username, 
KeyPair identity) throws IOException {
+        return sftpLogin(new InetSocketAddress(Objects.requireNonNull(host, 
"No host address"), port), username, identity);
+    }
+
+    /**
+     * Creates an SFTP session using the provided credentials
+     *
+     * @param target The target {@link SocketAddress}
+     * @param username Username
+     * @param password Password
+     * @return Created {@link SftpClient} - <B>Note:</B> closing the client 
also closes its
+     * underlying session
+     * @throws IOException If failed to login or authenticate
+     */
+    SftpClient sftpLogin(SocketAddress target, String username, String 
password) throws IOException;
+
+    /**
+     * Creates an SFTP session using the provided credentials
+     *
+     * @param target The target {@link SocketAddress}
+     * @param username Username
+     * @param identity The {@link KeyPair} identity
+     * @return Created {@link SftpClient} - <B>Note:</B> closing the client 
also closes its
+     * underlying session
+     * @throws IOException If failed to login or authenticate
+     */
+    SftpClient sftpLogin(SocketAddress target, String username, KeyPair 
identity) throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClientImpl.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClientImpl.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClientImpl.java
new file mode 100644
index 0000000..09a7007
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/client/simple/SimpleSftpClientImpl.java
@@ -0,0 +1,170 @@
+/*
+ * 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.client.simple;
+
+import java.io.IOException;
+import java.lang.reflect.Proxy;
+import java.net.SocketAddress;
+import java.security.KeyPair;
+
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpClientFactory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+public class SimpleSftpClientImpl extends AbstractLoggingBean implements 
SimpleSftpClient {
+
+    private SimpleClient client;
+    private SftpClientFactory sftpClientFactory;
+
+    public SimpleSftpClientImpl(SimpleClient client) {
+        this(client, null);
+    }
+
+    public SimpleSftpClientImpl(SimpleClient client, SftpClientFactory 
sftpClientFactory) {
+        this.client = client;
+        this.sftpClientFactory = sftpClientFactory != null ? sftpClientFactory 
: SftpClientFactory.instance();
+    }
+
+    public SimpleClient getClient() {
+        return client;
+    }
+
+    public void setClient(SimpleClient client) {
+        this.client = client;
+    }
+
+    public SftpClientFactory getSftpClientFactory() {
+        return sftpClientFactory;
+    }
+
+    public void setSftpClientFactory(SftpClientFactory sftpClientFactory) {
+        this.sftpClientFactory = sftpClientFactory;
+    }
+
+    @Override
+    public SftpClient sftpLogin(SocketAddress target, String username, String 
password) throws IOException {
+        return createSftpClient(client.sessionLogin(target, username, 
password));
+    }
+
+    @Override
+    public SftpClient sftpLogin(SocketAddress target, String username, KeyPair 
identity) throws IOException {
+        return createSftpClient(client.sessionLogin(target, username, 
identity));
+    }
+
+    protected SftpClient createSftpClient(final ClientSession session) throws 
IOException {
+        Exception err = null;
+        try {
+            SftpClient client = sftpClientFactory.createSftpClient(session);
+            try {
+                return createSftpClient(session, client);
+            } catch (Exception e) {
+                err = GenericUtils.accumulateException(err, e);
+                try {
+                    client.close();
+                } catch (Exception t) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("createSftpClient({}) failed ({}) to close 
client: {}",
+                                session, t.getClass().getSimpleName(), 
t.getMessage());
+                    }
+
+                    if (log.isTraceEnabled()) {
+                        log.trace("createSftpClient(" + session + ") client 
close failure details", t);
+                    }
+                    err = GenericUtils.accumulateException(err, t);
+                }
+            }
+        } catch (Exception e) {
+            err = GenericUtils.accumulateException(err, e);
+        }
+
+        // This point is reached if error occurred
+        log.warn("createSftpClient({}) failed ({}) to create session: {}",
+                session, err.getClass().getSimpleName(), err.getMessage());
+
+        try {
+            session.close();
+        } catch (Exception e) {
+            if (log.isDebugEnabled()) {
+                log.debug("createSftpClient({}) failed ({}) to close session: 
{}",
+                        session, e.getClass().getSimpleName(), e.getMessage());
+            }
+
+            if (log.isTraceEnabled()) {
+                log.trace("createSftpClient(" + session + ") session close 
failure details", e);
+            }
+            err = GenericUtils.accumulateException(err, e);
+        }
+
+        if (err instanceof IOException) {
+            throw (IOException) err;
+        } else {
+            throw new IOException(err);
+        }
+    }
+
+    protected SftpClient createSftpClient(final ClientSession session, final 
SftpClient client) throws IOException {
+        ClassLoader loader = getClass().getClassLoader();
+        Class<?>[] interfaces = {SftpClient.class};
+        return (SftpClient) Proxy.newProxyInstance(loader, interfaces, (proxy, 
method, args) -> {
+            Throwable err = null;
+            Object result = null;
+            String name = method.getName();
+            try {
+                result = method.invoke(client, args);
+            } catch (Throwable t) {
+                if (log.isTraceEnabled()) {
+                    log.trace("invoke(SftpClient#{}) failed ({}) to execute: 
{}",
+                            name, t.getClass().getSimpleName(), 
t.getMessage());
+                }
+                err = GenericUtils.accumulateException(err, t);
+            }
+
+            // propagate the "close" call to the session as well
+            if ("close".equals(name) && GenericUtils.isEmpty(args)) {
+                try {
+                    session.close();
+                } catch (Throwable t) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("invoke(ClientSession#{}) failed ({}) to 
execute: {}",
+                                name, t.getClass().getSimpleName(), 
t.getMessage());
+                    }
+                    err = GenericUtils.accumulateException(err, t);
+                }
+            }
+
+            if (err != null) {
+                throw err;
+            }
+
+            return result;
+        });
+    }
+
+    @Override
+    public boolean isOpen() {
+        return true;
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/RawSftpClient.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/RawSftpClient.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/RawSftpClient.java
new file mode 100644
index 0000000..676a03e
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/RawSftpClient.java
@@ -0,0 +1,44 @@
+/*
+ * 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.client.subsystem.sftp;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public interface RawSftpClient {
+    /**
+     * @param cmd    Command to send - <B>Note:</B> only lower 8-bits are used
+     * @param buffer The {@link Buffer} containing the command data
+     * @return The assigned request id
+     * @throws IOException if failed to send command
+     */
+    int send(int cmd, Buffer buffer) throws IOException;
+
+    /**
+     * @param id The expected request id
+     * @return The received response {@link Buffer} containing the request id
+     * @throws IOException If connection closed or interrupted
+     */
+    Buffer receive(int id) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java
----------------------------------------------------------------------
diff --git 
a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java
 
b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java
new file mode 100644
index 0000000..7cada6e
--- /dev/null
+++ 
b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpAclFileAttributeView.java
@@ -0,0 +1,67 @@
+/*
+ * 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.client.subsystem.sftp;
+
+import java.io.IOException;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.AclEntry;
+import java.nio.file.attribute.AclFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.UserPrincipal;
+import java.util.List;
+
+import 
org.apache.sshd.client.subsystem.sftp.impl.AbstractSftpFileAttributeView;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class SftpAclFileAttributeView extends AbstractSftpFileAttributeView 
implements AclFileAttributeView {
+    public SftpAclFileAttributeView(SftpFileSystemProvider provider, Path 
path, LinkOption... options) {
+        super(provider, path, options);
+    }
+
+    @Override
+    public UserPrincipal getOwner() throws IOException {
+        PosixFileAttributes v = provider.readAttributes(path, 
PosixFileAttributes.class, options);
+        return v.owner();
+    }
+
+    @Override
+    public void setOwner(UserPrincipal owner) throws IOException {
+        provider.setAttribute(path, "posix", "owner", owner, options);
+    }
+
+    @Override
+    public String name() {
+        return "acl";
+    }
+
+    @Override
+    public List<AclEntry> getAcl() throws IOException {
+        return readRemoteAttributes().getAcl();
+    }
+
+    @Override
+    public void setAcl(List<AclEntry> acl) throws IOException {
+        writeRemoteAttributes(new SftpClient.Attributes().acl(acl));
+    }
+
+}

Reply via email to