You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/06/23 08:34:22 UTC

[2/2] mina-sshd git commit: [SSHD-501] Add 'verify' with timeout method to client ConnectFuture

[SSHD-501] Add 'verify' with timeout method to client ConnectFuture


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

Branch: refs/heads/master
Commit: 8a16e8cc8e1f16c41c952f29022588144547661a
Parents: 6f8507a
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Tue Jun 23 09:34:08 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Tue Jun 23 09:34:08 2015 +0300

----------------------------------------------------------------------
 .../java/org/apache/sshd/client/SshClient.java  |  4 +-
 .../apache/sshd/client/future/AuthFuture.java   | 14 ++--
 .../sshd/client/future/ConnectFuture.java       |  7 ++
 .../sshd/client/future/DefaultAuthFuture.java   | 30 +++----
 .../client/future/DefaultConnectFuture.java     | 33 ++++++--
 .../sshd/client/future/DefaultOpenFuture.java   | 34 ++++----
 .../apache/sshd/client/future/OpenFuture.java   | 21 ++---
 .../sshd/client/scp/DefaultScpClient.java       | 25 +-----
 .../sshd/client/sftp/DefaultSftpClient.java     | 28 +++----
 .../common/channel/BufferedIoOutputStream.java  |  8 +-
 .../common/channel/ChannelAsyncInputStream.java | 31 ++++---
 .../channel/ChannelAsyncOutputStream.java       | 46 +----------
 .../sshd/common/channel/IoWriteFutureImpl.java  | 41 +++++++++
 .../sshd/common/future/DefaultCloseFuture.java  |  5 +-
 .../sshd/common/future/DefaultSshFuture.java    | 87 ++++++++++++++++----
 .../apache/sshd/common/future/SshFuture.java    | 24 ++++--
 .../sshd/common/io/AbstractIoWriteFuture.java   | 69 ++++++++++++++++
 .../org/apache/sshd/common/io/IoReadFuture.java | 22 ++---
 .../apache/sshd/common/io/IoWriteFuture.java    | 12 +--
 .../apache/sshd/common/io/mina/MinaSession.java | 59 ++++---------
 .../io/nio2/Nio2DefaultIoWriteFuture.java       | 49 +++++++++++
 .../apache/sshd/common/io/nio2/Nio2Service.java |  4 +-
 .../apache/sshd/common/io/nio2/Nio2Session.java | 58 ++-----------
 .../sshd/common/session/AbstractSession.java    | 62 --------------
 .../sshd/common/session/PendingWriteFuture.java | 61 ++++++++++++++
 .../apache/sshd/common/util/CloseableUtils.java |  7 +-
 .../apache/sshd/common/util/GenericUtils.java   | 19 +++++
 .../java/org/apache/sshd/server/SshServer.java  |  6 +-
 .../org/apache/sshd/AuthenticationTest.java     | 11 +--
 .../java/org/apache/sshd/KeepAliveTest.java     | 12 +--
 .../java/org/apache/sshd/KeyReExchangeTest.java |  4 +-
 .../src/test/java/org/apache/sshd/LoadTest.java |  2 +-
 .../org/apache/sshd/PortForwardingTest.java     |  2 +-
 .../test/java/org/apache/sshd/ProxyTest.java    |  2 +-
 .../apache/sshd/SinglePublicKeyAuthTest.java    |  5 +-
 .../java/org/apache/sshd/WelcomeBannerTest.java |  2 +-
 .../java/org/apache/sshd/WindowAdjustTest.java  |  3 +-
 .../test/java/org/apache/sshd/WindowTest.java   |  6 +-
 .../java/org/apache/sshd/agent/AgentTest.java   |  4 +-
 .../java/org/apache/sshd/client/ClientTest.java | 48 +++++------
 .../org/apache/sshd/client/kex/KexTest.java     |  2 +-
 .../org/apache/sshd/client/scp/ScpTest.java     | 22 ++---
 .../org/apache/sshd/client/sftp/SftpTest.java   |  8 +-
 .../AbstractSignatureFactoryTestSupport.java    |  2 +-
 .../deprecated/ClientUserAuthServiceOld.java    | 12 +--
 .../java/org/apache/sshd/server/ServerTest.java | 16 ++--
 46 files changed, 565 insertions(+), 464 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
index f50eede..7df53e7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
@@ -208,8 +208,8 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
 
     public void stop() {
         try {
-            close(true).await();
-        } catch (InterruptedException e) {
+            close(true).await();    // TODO use verify + configurable timeout
+        } catch (IOException e) {
             log.debug("Exception caught while stopping client", e);
         }
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/client/future/AuthFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/future/AuthFuture.java b/sshd-core/src/main/java/org/apache/sshd/client/future/AuthFuture.java
index 99adeab..11fd830 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/future/AuthFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/future/AuthFuture.java
@@ -18,9 +18,9 @@
  */
 package org.apache.sshd.client.future;
 
+import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.future.SshFuture;
 
 /**
@@ -31,21 +31,21 @@ import org.apache.sshd.common.future.SshFuture;
 public interface AuthFuture extends SshFuture<AuthFuture> {
     /**
      * Wait and verify that the authentication succeeded.
-     * @throws SshException if the authentication failed for any reason
+     * @throws IOException if the authentication failed for any reason
      */
-    void verify() throws SshException;
+    void verify() throws IOException;
 
     /**
      * Wait and verify that the authentication succeeded within the specified timeout.
-     * @throws SshException if the authentication failed for any reason
+     * @throws IOException if the authentication failed for any reason
      */
-    void verify(long timeout, TimeUnit unit) throws SshException;
+    void verify(long timeout, TimeUnit unit) throws IOException;
 
     /**
      * Wait and verify that the authentication succeeded within the specified timeout.
-     * @throws SshException if the authentication failed for any reason
+     * @throws IOException if the authentication failed for any reason
      */
-    void verify(long timeoutMillis) throws SshException;
+    void verify(long timeoutMillis) throws IOException;
 
     /**
      * Returns the cause of the connection failure.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/client/future/ConnectFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/future/ConnectFuture.java b/sshd-core/src/main/java/org/apache/sshd/client/future/ConnectFuture.java
index 649bde8..3bbefa9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/future/ConnectFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/future/ConnectFuture.java
@@ -18,6 +18,9 @@
  */
 package org.apache.sshd.client.future;
 
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.future.SshFuture;
 
@@ -30,6 +33,10 @@ public interface ConnectFuture extends SshFuture<ConnectFuture> {
 
     ClientSession getSession();
 
+    // wait and verify that connection succeeded within specified timeout
+    ConnectFuture verify(long count, TimeUnit unit) throws IOException;
+    ConnectFuture verify(long timeout) throws IOException;
+    
     /**
      * Returns the cause of the connection failure.
      *

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultAuthFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultAuthFuture.java b/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultAuthFuture.java
index 4fc29c6..ca4d9d7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultAuthFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultAuthFuture.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sshd.client.future;
 
+import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.common.SshException;
@@ -37,31 +38,24 @@ public class DefaultAuthFuture extends DefaultSshFuture<AuthFuture> implements A
     }
 
     @Override   // TODO for JDK-8 make this a default method
-    public void verify() throws SshException {
+    public void verify() throws IOException {
         verify(Long.MAX_VALUE);
     }
 
     @Override   // TODO for JDK-8 make this a default method
-    public void verify(long timeout, TimeUnit unit) throws SshException {
+    public void verify(long timeout, TimeUnit unit) throws IOException {
         verify(unit.toMillis(timeout));        
     }
 
     @Override
-    public void verify(long timeoutMillis) throws SshException {
-        try {
-            if (!await(timeoutMillis)) {
-                throw new SshException("Authentication timeout afer " + timeoutMillis);
-            }
-        } catch (InterruptedException e) {
-            throw new SshException("Authentication interrupted", e);
-        }
-
-        if (!isSuccess()) {
-            throw new SshException("Authentication failed", getException());
+    public void verify(long timeoutMillis) throws IOException {
+        Boolean result = verifyResult(Boolean.class, timeoutMillis);
+        if (!result.booleanValue()) {
+            throw new SshException("Authentication failed");
         }
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public Throwable getException() {
         Object v = getValue();
         if (v instanceof Throwable) {
@@ -71,24 +65,24 @@ public class DefaultAuthFuture extends DefaultSshFuture<AuthFuture> implements A
         }
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public boolean isSuccess() {
         Object v = getValue();
         return (v instanceof Boolean) && ((Boolean) v).booleanValue();
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public boolean isFailure() {
         Object v = getValue();
         return (v instanceof Boolean) && (!((Boolean) v).booleanValue());
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public void setAuthed(boolean authed) {
         setValue(Boolean.valueOf(authed));
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public void setException(Throwable exception) {
         ValidateUtils.checkNotNull(exception, "No exception provided", GenericUtils.EMPTY_OBJECT_ARRAY);
         setValue(exception);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultConnectFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultConnectFuture.java b/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultConnectFuture.java
index bd02880..35d4867 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultConnectFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultConnectFuture.java
@@ -18,9 +18,13 @@
  */
 package org.apache.sshd.client.future;
 
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.RuntimeSshException;
 import org.apache.sshd.common.future.DefaultSshFuture;
+import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 
@@ -30,12 +34,28 @@ import org.apache.sshd.common.util.ValidateUtils;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public class DefaultConnectFuture extends DefaultSshFuture<ConnectFuture> implements ConnectFuture {
-
     public DefaultConnectFuture(Object lock) {
         super(lock);
     }
 
-    @Override
+    @Override   // TODO in JDK-8 make this a default method
+    public ConnectFuture verify(long count, TimeUnit unit) throws IOException {
+        return verify(unit.toMillis(count));
+    }
+    
+    @Override   // TODO in JDK-8 make this a default method
+    public ConnectFuture verify(long timeout) throws IOException {
+        long startTime = System.nanoTime();
+        ClientSession session = verifyResult(ClientSession.class, timeout);
+        long endTime = System.nanoTime();
+        if (log.isDebugEnabled()) {
+            IoSession ioSession = session.getIoSession();
+            log.debug("Connected to " + ioSession.getRemoteAddress() + " after " + (endTime - startTime) + " nanos");
+        }
+        return this;
+    }
+
+    @Override   // TODO in JDK-8 make this a default method
     public ClientSession getSession() {
         Object v = getValue();
         if (v instanceof RuntimeException) {
@@ -51,7 +71,7 @@ public class DefaultConnectFuture extends DefaultSshFuture<ConnectFuture> implem
         }
     }
 
-    @Override
+    @Override   // TODO in JDK-8 make this a default method
     public Throwable getException() {
         Object v = getValue();
         if (v instanceof Throwable) {
@@ -61,21 +81,20 @@ public class DefaultConnectFuture extends DefaultSshFuture<ConnectFuture> implem
         }
     }
 
-    @Override
+    @Override   // TODO in JDK-8 make this a default method
     public boolean isConnected() {
         return getValue() instanceof ClientSession;
     }
 
-    @Override
+    @Override   // TODO in JDK-8 make this a default method
     public void setSession(ClientSession session) {
         ValidateUtils.checkNotNull(session, "No client session provided", GenericUtils.EMPTY_OBJECT_ARRAY);
         setValue(session);
     }
 
-    @Override
+    @Override   // TODO in JDK-8 make this a default method
     public void setException(Throwable exception) {
         ValidateUtils.checkNotNull(exception, "No exception provided", GenericUtils.EMPTY_OBJECT_ARRAY);
         setValue(exception);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultOpenFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultOpenFuture.java b/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultOpenFuture.java
index 7dc401f..f1d4674 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultOpenFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/future/DefaultOpenFuture.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sshd.client.future;
 
+import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.common.SshException;
@@ -36,31 +37,24 @@ public class DefaultOpenFuture extends DefaultSshFuture<OpenFuture> implements O
     }
 
     @Override   // TODO for JDK-8 make this a default method
-    public void verify() throws SshException {
+    public void verify() throws IOException {
         verify(Long.MAX_VALUE);
     }
 
     @Override   // TODO for JDK-8 make this a default method
-    public void verify(long timeout, TimeUnit unit) throws SshException {
+    public void verify(long timeout, TimeUnit unit) throws IOException {
         verify(unit.toMillis(timeout));        
     }
 
-    @Override
-    public void verify(long timeoutMillis) throws SshException {
-        try {
-            if (!await(timeoutMillis)) {
-                throw new SshException("Channel opening time out after " + timeoutMillis);
-            }
-        } catch (InterruptedException e) {
-            throw new SshException("Channel opening interrupted", e);
-        }
-
-        if (!isOpened()) {
-            throw new SshException("Channel opening failed", getException());
+    @Override   // TODO for JDK-8 make this a default method
+    public void verify(long timeoutMillis) throws IOException {
+        Boolean result = verifyResult(Boolean.class, timeoutMillis);
+        if (!result.booleanValue()) {
+            throw new SshException("Channel opening failed");
         }
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public Throwable getException() {
         Object v = getValue();
         if (v instanceof Throwable) {
@@ -70,20 +64,20 @@ public class DefaultOpenFuture extends DefaultSshFuture<OpenFuture> implements O
         }
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public boolean isOpened() {
-        return getValue() instanceof Boolean;
+        Object value = getValue();
+        return (value instanceof Boolean) && ((Boolean) value).booleanValue();
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public void setOpened() {
         setValue(Boolean.TRUE);
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public void setException(Throwable exception) {
         ValidateUtils.checkNotNull(exception, "No exception provided", GenericUtils.EMPTY_OBJECT_ARRAY);
         setValue(exception);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/client/future/OpenFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/future/OpenFuture.java b/sshd-core/src/main/java/org/apache/sshd/client/future/OpenFuture.java
index fcb3ae9..b80dc03 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/future/OpenFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/future/OpenFuture.java
@@ -18,9 +18,9 @@
  */
 package org.apache.sshd.client.future;
 
+import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.future.SshFuture;
 
 /**
@@ -29,24 +29,13 @@ import org.apache.sshd.common.future.SshFuture;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public interface OpenFuture extends SshFuture<OpenFuture> {
-
     /**
      * Wait and verify that the channel has been successfully opened.
-     * @throws SshException if the action failed for any reason
-     */
-    void verify() throws SshException;
-
-    /**
-     * Wait and verify that the channel has been successfully opened within the specified timeout.
-     * @throws SshException if the action failed for any reason
-     */
-    void verify(long timeout, TimeUnit unit) throws SshException;
-
-    /**
-     * Wait and verify that the authentication succeeded within the specified timeout.
-     * @throws SshException if the action failed for any reason
+     * @throws IOException if the action failed for any reason
      */
-    void verify(long timeoutMillis) throws SshException;
+    void verify() throws IOException;
+    void verify(long timeout, TimeUnit unit) throws IOException;
+    void verify(long timeoutMillis) throws IOException;
 
     /**
      * Returns the cause of the connection failure.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
index b7d0990..15ca7cb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/scp/DefaultScpClient.java
@@ -20,7 +20,6 @@ package org.apache.sshd.client.scp;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InterruptedIOException;
 import java.io.OutputStream;
 import java.nio.file.FileSystem;
 import java.nio.file.Path;
@@ -82,11 +81,7 @@ public class DefaultScpClient extends AbstractScpClient {
         String cmd = createReceiveCommand(remote, Collections.<Option>emptyList());
         ChannelExec channel = clientSession.createExecChannel(cmd);
         try {
-            try {
-                channel.open().await();
-            } catch (InterruptedException e) {
-                throw (IOException) new InterruptedIOException("Interrupted while await channel open for download of " + remote).initCause(e);
-            }
+            channel.open().await(); // TODO use verify + configurable timeout
 
             // NOTE: we use a mock file system since we expect no invocations for it
             ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), new MockFileSystem(remote), listener);
@@ -101,11 +96,7 @@ public class DefaultScpClient extends AbstractScpClient {
         String cmd = createReceiveCommand(remote, options);
         ChannelExec channel = clientSession.createExecChannel(cmd);
         try {
-            try {
-                channel.open().await();
-            } catch (InterruptedException e) {
-                throw (IOException) new InterruptedIOException("Interrupted while await channel open for download of " + remote + " to " + local).initCause(e);
-            }
+            channel.open().await(); // TODO use verify + configurable timeout
 
             ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), fs, listener);
             helper.receive(local,
@@ -127,11 +118,7 @@ public class DefaultScpClient extends AbstractScpClient {
                           ;
         final String cmd = createSendCommand(remote, (time != null) ? EnumSet.of(Option.PreserveAttributes) : Collections.<Option>emptySet());
         ChannelExec channel = clientSession.createExecChannel(cmd);
-        try {
-            channel.open().await();
-        } catch (InterruptedException e) {
-            throw (IOException) new InterruptedIOException("Interrupted while await channel open for stream upload to " + remote).initCause(e);
-        }
+        channel.open().await();   // TODO use verify + configurable timeout
 
         try {
             ScpHelper helper = new ScpHelper(channel.getInvertedOut(), channel.getInvertedIn(), new MockFileSystem(remote), listener);
@@ -188,11 +175,7 @@ public class DefaultScpClient extends AbstractScpClient {
         
         String cmd = createSendCommand(remote, options);
         ChannelExec channel = clientSession.createExecChannel(cmd);
-        try {
-            channel.open().await();
-        } catch (InterruptedException e) {
-            throw (IOException) new InterruptedIOException("Interrupted while await channel open for upload to " + remote).initCause(e);
-        }
+        channel.open().await();    // TODO use verify + configurable timeout
 
         try {
             FactoryManager manager = clientSession.getFactoryManager();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java b/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
index 809bc91..e810ffb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/sftp/DefaultSftpClient.java
@@ -134,22 +134,18 @@ public class DefaultSftpClient extends AbstractSftpClient {
         this.clientSession = clientSession;
         this.channel = clientSession.createSubsystemChannel(SftpConstants.SFTP_SUBSYSTEM_NAME);
         this.messages = new HashMap<>();
-        try {
-            this.channel.setOut(new OutputStream() {
-                @Override
-                public void write(int b) throws IOException {
-                    write(new byte[] { (byte) b }, 0, 1);
-                }
-                @Override
-                public void write(byte[] b, int off, int len) throws IOException {
-                    data(b, off, len);
-                }
-            });
-            this.channel.setErr(new ByteArrayOutputStream());
-            this.channel.open().await();
-        } catch (InterruptedException e) {
-            throw (IOException) new InterruptedIOException("Interrupted while await channel open").initCause(e);
-        }
+        this.channel.setOut(new OutputStream() {
+            @Override
+            public void write(int b) throws IOException {
+                write(new byte[] { (byte) b }, 0, 1);
+            }
+            @Override
+            public void write(byte[] b, int off, int len) throws IOException {
+                data(b, off, len);
+            }
+        });
+        this.channel.setErr(new ByteArrayOutputStream(Byte.MAX_VALUE));
+        this.channel.open().await();       // TODO use verify + configurable timeout
         this.channel.onClose(new Runnable() {
             @SuppressWarnings("synthetic-access")
             @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/channel/BufferedIoOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/BufferedIoOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/BufferedIoOutputStream.java
index 6ea875a..9a622ca 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/BufferedIoOutputStream.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/BufferedIoOutputStream.java
@@ -36,8 +36,8 @@ import org.apache.sshd.common.util.buffer.Buffer;
 public class BufferedIoOutputStream extends CloseableUtils.AbstractInnerCloseable implements IoOutputStream {
 
     private final IoOutputStream out;
-    private final Queue<ChannelAsyncOutputStream.IoWriteFutureImpl> writes = new ConcurrentLinkedQueue<ChannelAsyncOutputStream.IoWriteFutureImpl>();
-    private final AtomicReference<ChannelAsyncOutputStream.IoWriteFutureImpl> currentWrite = new AtomicReference<ChannelAsyncOutputStream.IoWriteFutureImpl>();
+    private final Queue<IoWriteFutureImpl> writes = new ConcurrentLinkedQueue<IoWriteFutureImpl>();
+    private final AtomicReference<IoWriteFutureImpl> currentWrite = new AtomicReference<IoWriteFutureImpl>();
 
     public BufferedIoOutputStream(IoOutputStream out) {
         this.out = out;
@@ -45,7 +45,7 @@ public class BufferedIoOutputStream extends CloseableUtils.AbstractInnerCloseabl
 
     @Override
     public IoWriteFuture write(Buffer buffer) {
-        final ChannelAsyncOutputStream.IoWriteFutureImpl future = new ChannelAsyncOutputStream.IoWriteFutureImpl(buffer);
+        final IoWriteFutureImpl future = new IoWriteFutureImpl(buffer);
         if (isClosing()) {
             future.setValue(new IOException("Closed"));
         } else {
@@ -56,7 +56,7 @@ public class BufferedIoOutputStream extends CloseableUtils.AbstractInnerCloseabl
     }
 
     private void startWriting() {
-        final ChannelAsyncOutputStream.IoWriteFutureImpl future = writes.peek();
+        final IoWriteFutureImpl future = writes.peek();
         if (future != null) {
             if (currentWrite.compareAndSet(null, future)) {
                 out.write(future.getBuffer()).addListener(new SshFutureListener<IoWriteFuture>() {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelAsyncInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelAsyncInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelAsyncInputStream.java
index e888949..bad2831 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelAsyncInputStream.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelAsyncInputStream.java
@@ -135,29 +135,26 @@ public class ChannelAsyncInputStream extends CloseableUtils.AbstractCloseable im
         }
 
         @Override   // TODO for JDK-8 make this a default method
-        public void verify() throws SshException {
+        public void verify() throws IOException {
             verify(Long.MAX_VALUE);
         }
 
         @Override   // TODO for JDK-8 make this a default method
-        public void verify(long timeout, TimeUnit unit) throws SshException {
+        public void verify(long timeout, TimeUnit unit) throws IOException {
             verify(unit.toMillis(timeout));        
         }
 
-        @Override
-        public void verify(long timeoutMillis) throws SshException {
-            try {
-                if (!await(timeoutMillis)) {
-                    throw new SshException("Timed out after " + timeoutMillis);
-                }
-            } catch (InterruptedException e) {
-                throw new SshException("Interrupted", e);
-            }
-            if (getValue() instanceof Throwable) {
-                throw new SshException("Write failed", getException());
+        @Override   // TODO for JDK-8 make this a default method
+        public void verify(long timeoutMillis) throws IOException {
+            long startTime = System.nanoTime();
+            Number result = verifyResult(Number.class, timeoutMillis);
+            long endTime = System.nanoTime();
+            if (log.isDebugEnabled()) {
+                log.debug("Read " + result + " bytes after " + (endTime - startTime) + " nanos");
             }
         }
-        @Override
+
+        @Override   // TODO for JDK-8 make this a default method
         public int getRead() {
             Object v = getValue();
             if (v instanceof RuntimeException) {
@@ -166,14 +163,14 @@ public class ChannelAsyncInputStream extends CloseableUtils.AbstractCloseable im
                 throw (Error) v;
             } else if (v instanceof Throwable) {
                 throw (RuntimeSshException) new RuntimeSshException("Error reading from channel.").initCause((Throwable) v);
-            } else if (v instanceof Integer) {
-                return ((Integer) v).intValue();
+            } else if (v instanceof Number) {
+                return ((Number) v).intValue();
             } else {
                 throw new IllegalStateException("Unknown read value type: " + ((v == null) ? "null" : v.getClass().getName()));
             }
         }
 
-        @Override
+        @Override   // TODO for JDK-8 make this a default method
         public Throwable getException() {
             Object v = getValue();
             if (v instanceof Throwable) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelAsyncOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelAsyncOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelAsyncOutputStream.java
index c0da012..5ee174d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelAsyncOutputStream.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelAsyncOutputStream.java
@@ -22,9 +22,7 @@ import java.io.IOException;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultSshFuture;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.io.IoOutputStream;
 import org.apache.sshd.common.io.IoWriteFuture;
@@ -69,7 +67,7 @@ public class ChannelAsyncOutputStream extends CloseableUtils.AbstractCloseable i
     protected synchronized void doWriteIfPossible(boolean resume) {
         final IoWriteFutureImpl future = pendingWrite.get();
         if (future != null) {
-            final Buffer buffer = future.buffer;
+            final Buffer buffer = future.getBuffer();
             final int total = buffer.available();
             if (total > 0) {
                 final int length = Math.min(Math.min(channel.getRemoteWindow().getSize(), total), channel.getRemoteWindow().getPacketSize());
@@ -116,46 +114,4 @@ public class ChannelAsyncOutputStream extends CloseableUtils.AbstractCloseable i
     public String toString() {
         return "ChannelAsyncOutputStream[" + channel + "]";
     }
-
-    public static class IoWriteFutureImpl extends DefaultSshFuture<IoWriteFuture> implements IoWriteFuture {
-
-        final Buffer buffer;
-
-        public IoWriteFutureImpl(Buffer buffer) {
-            super(null);
-            this.buffer = buffer;
-        }
-
-        public Buffer getBuffer() {
-            return buffer;
-        }
-
-        @Override
-        public void verify() throws SshException {
-            try {
-                await();
-            }
-            catch (InterruptedException e) {
-                throw new SshException("Interrupted", e);
-            }
-            if (!isWritten()) {
-                throw new SshException("Write failed", getException());
-            }
-        }
-
-        @Override
-        public boolean isWritten() {
-            return getValue() instanceof Boolean;
-        }
-
-        @Override
-        public Throwable getException() {
-            Object v = getValue();
-            if (v instanceof Throwable) {
-                return (Throwable) v;
-            } else {
-                return null;
-            }
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/channel/IoWriteFutureImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/IoWriteFutureImpl.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/IoWriteFutureImpl.java
new file mode 100644
index 0000000..26cb8eb
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/IoWriteFutureImpl.java
@@ -0,0 +1,41 @@
+/*
+ * 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.channel;
+
+import org.apache.sshd.common.io.AbstractIoWriteFuture;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class IoWriteFutureImpl extends AbstractIoWriteFuture {
+    private final Buffer buffer;
+
+    public IoWriteFutureImpl(Buffer buffer) {
+        super(null);
+        this.buffer = ValidateUtils.checkNotNull(buffer, "No buffer provided", GenericUtils.EMPTY_OBJECT_ARRAY);
+    }
+
+    public Buffer getBuffer() {
+        return buffer;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
index 0092033..2f64ca9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
@@ -32,7 +32,7 @@ public class DefaultCloseFuture extends DefaultSshFuture<CloseFuture> implements
         super(lock);
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public boolean isClosed() {
         if (isDone()) {
             return ((Boolean) getValue()).booleanValue();
@@ -41,9 +41,8 @@ public class DefaultCloseFuture extends DefaultSshFuture<CloseFuture> implements
         }
     }
 
-    @Override
+    @Override   // TODO for JDK-8 make this a default method
     public void setClosed() {
         setValue(Boolean.TRUE);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
index 39ae862..46cf133 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
@@ -18,9 +18,13 @@
  */
 package org.apache.sshd.common.future;
 
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.StreamCorruptedException;
 import java.lang.reflect.Array;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.logging.AbstractLoggingBean;
@@ -49,21 +53,21 @@ public class DefaultSshFuture<T extends SshFuture> extends AbstractLoggingBean i
     }
 
     @Override
-    public T await() throws InterruptedException {
-        if (await0(Long.MAX_VALUE, true) == null) {
-            throw new InternalError("No result while await completion");
+    public T await() throws IOException {
+        if (await(Long.MAX_VALUE)) {
+            return asT();
+        } else {
+            throw new SshException("No result while await completion");
         }
-
-        return asT();
     }
 
     @Override
-    public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
+    public boolean await(long timeout, TimeUnit unit) throws IOException {
         return await(unit.toMillis(timeout));
     }
 
     @Override
-    public boolean await(long timeoutMillis) throws InterruptedException {
+    public boolean await(long timeoutMillis) throws IOException {
         return await0(timeoutMillis, true) != null;
     }
 
@@ -71,7 +75,7 @@ public class DefaultSshFuture<T extends SshFuture> extends AbstractLoggingBean i
     public T awaitUninterruptibly() {
         try {
             await0(Long.MAX_VALUE, false);
-        } catch ( InterruptedException ie) {
+        } catch (InterruptedIOException ie) {
             // Do nothing : this catch is just mandatory by contract
         }
 
@@ -87,23 +91,77 @@ public class DefaultSshFuture<T extends SshFuture> extends AbstractLoggingBean i
     public boolean awaitUninterruptibly(long timeoutMillis) {
         try {
             return await0(timeoutMillis, false) != null;
-        } catch (InterruptedException e) {
+        } catch (InterruptedIOException e) {
             throw new InternalError("Unexpected interrupted exception wile awaitUninterruptibly " + timeoutMillis + " msec.: " + e.getMessage(), e);
         }
     }
 
     /**
+     * <P>Waits (interruptible) for the specified timeout (msec.) and then checks
+     * the result:</P><BR/>
+     * <UL>
+     *      <LI>
+     *      If result is {@code null} then timeout is assumed to have expired - throw
+     *      an appropriate {@link IOException}
+     *      </LI>
+     *      
+     *      <LI>
+     *      If the result is of the expected type, then cast and return it
+     *      </LI>
+     *      
+     *      <LI>
+     *      If the result is an {@link IOException} then re-throw it
+     *      </LI>
+     *      
+     *      <LI>
+     *      If the result is a {@link Throwable} then throw an {@link IOException}
+     *      whose cause is the original exception
+     *      </LI>
+     *      
+     *      <LI>
+     *      Otherwise (should never happen), throw a {@link StreamCorruptedException}
+     *      with the name of the result type
+     *      </LI>
+     * </UL>
+     * @param expectedType The expected result type
+     * @param timeout The timeout (millis) to wait for a result
+     * @return The (never {@code null}) result
+     * @throws IOException If failed to retrieve the expected result on time
+     */
+    protected <R> R verifyResult(Class<? extends R> expectedType, long timeout) throws IOException {
+        Object value = await0(timeout, true);
+        if (value == null) {
+            throw new SshException("Failed to get operation result within specified timeout: " + timeout);
+        }
+        
+        Class<?> actualType = value.getClass();
+        if (expectedType.isAssignableFrom(actualType)) {
+            return expectedType.cast(value);
+        } else if (IOException.class.isAssignableFrom(actualType)) {
+            throw (IOException) value;
+        } else if (Throwable.class.isAssignableFrom(actualType)) {
+            Throwable t = (Throwable) value;
+            throw new SshException("Failed (" + t.getClass().getSimpleName() + ") to execute: " + t.getMessage(), GenericUtils.resolveExceptionCause(t));
+        } else {    // what else can it be ????
+            throw new StreamCorruptedException("Unknown result type: " + actualType.getName());
+        }
+    }
+
+    /**
      * Wait for the Future to be ready. If the requested delay is 0 or
      * negative, this method immediately returns.
      * @param timeoutMillis The delay we will wait for the Future to be ready
-     * @param interruptable Tells if the wait can be interrupted or not
+     * @param interruptable Tells if the wait can be interrupted or not.
+     * If {@code true} and the thread is interrupted then an {@link InterruptedIOException}
+     * is thrown.
      * @return The non-{@code null} result object if the Future is ready,
      * {@code null} if the timeout expired and no result was received
-     * @throws InterruptedException If the thread has been interrupted
+     * @throws InterruptedIOException If the thread has been interrupted
      * when it's not allowed.
      */
-    protected Object await0(long timeoutMillis, boolean interruptable) throws InterruptedException {
-        long curTime = System.currentTimeMillis();
+    protected Object await0(long timeoutMillis, boolean interruptable) throws InterruptedIOException {
+        ValidateUtils.checkTrue(timeoutMillis >= 0L, "Negative timeout N/A: %d", timeoutMillis);
+        long startTime = System.currentTimeMillis(), curTime = startTime;
         long endTime = ((Long.MAX_VALUE - timeoutMillis) < curTime) ? Long.MAX_VALUE : (curTime + timeoutMillis);
 
         synchronized (lock) {
@@ -116,7 +174,8 @@ public class DefaultSshFuture<T extends SshFuture> extends AbstractLoggingBean i
                     lock.wait(endTime - curTime);
                 } catch (InterruptedException e) {
                     if (interruptable) {
-                        throw e;
+                        curTime = System.currentTimeMillis();
+                        throw (InterruptedIOException) new InterruptedIOException("Interrupted after " + (curTime - startTime) + " msec.").initCause(e);
                     }
                 }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java
index 5e351d2..ecb5d07 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sshd.common.future;
 
+import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -33,22 +34,27 @@ public interface SshFuture<T extends SshFuture> {
      * Wait for the asynchronous operation to complete.
      * The attached listeners will be notified when the operation is
      * completed.
+     * @return The {@code this} instance
+     * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
+     * if waiting was interrupted
      */
-    T await() throws InterruptedException;
+    T await() throws IOException;
 
     /**
      * Wait for the asynchronous operation to complete with the specified timeout.
-     *
-     * @return <tt>true</tt> if the operation is completed.
+     * @return {@code true} if the operation is completed.
+     * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
+     * if waiting was interrupted
      */
-    boolean await(long timeout, TimeUnit unit) throws InterruptedException;
+    boolean await(long timeout, TimeUnit unit) throws IOException;
 
     /**
      * Wait for the asynchronous operation to complete with the specified timeout.
-     *
-     * @return <tt>true</tt> if the operation is completed.
+     * @return {@code true} if the operation is completed.
+     * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
+     * if waiting was interrupted
      */
-    boolean await(long timeoutMillis) throws InterruptedException;
+    boolean await(long timeoutMillis) throws IOException;
 
     /**
      * Wait for the asynchronous operation to complete uninterruptibly.
@@ -63,7 +69,7 @@ public interface SshFuture<T extends SshFuture> {
      * Wait for the asynchronous operation to complete with the specified timeout
      * uninterruptibly.
      *
-     * @return <tt>true</tt> if the operation is completed.
+     * @return {@code true} if the operation is completed.
      */
     boolean awaitUninterruptibly(long timeout, TimeUnit unit);
 
@@ -71,7 +77,7 @@ public interface SshFuture<T extends SshFuture> {
      * Wait for the asynchronous operation to complete with the specified timeout
      * uninterruptibly.
      *
-     * @return <tt>true</tt> if the operation is finished.
+     * @return {@code true} if the operation is finished.
      */
     boolean awaitUninterruptibly(long timeoutMillis);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/io/AbstractIoWriteFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/AbstractIoWriteFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/io/AbstractIoWriteFuture.java
new file mode 100644
index 0000000..b67b1ff
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/AbstractIoWriteFuture.java
@@ -0,0 +1,69 @@
+/*
+ * 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.io;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.future.DefaultSshFuture;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractIoWriteFuture extends DefaultSshFuture<IoWriteFuture> implements IoWriteFuture {
+    protected AbstractIoWriteFuture(Object lock) {
+        super(lock);
+    }
+
+    @Override      // TODO for JDK-8 make this a default method
+    public void verify() throws IOException {
+        verify(Long.MAX_VALUE);
+    }
+
+    @Override      // TODO for JDK-8 make this a default method
+    public void verify(long count, TimeUnit unit) throws IOException {
+        verify(unit.toMillis(count));
+    }
+
+    @Override      // TODO for JDK-8 make this a default method
+    public void verify(long timeout) throws IOException {
+        Boolean result = verifyResult(Boolean.class, timeout);
+        if (!result.booleanValue()) {
+            throw new SshException("Write failed signalled");
+        }
+    }
+
+    @Override      // TODO for JDK-8 make this a default method
+    public boolean isWritten() {
+        Object value = getValue();
+        return (value instanceof Boolean) && ((Boolean) value).booleanValue();
+    }
+
+    @Override      // TODO for JDK-8 make this a default method
+    public Throwable getException() {
+        Object v = getValue();
+        if (v instanceof Throwable) {
+            return (Throwable) v;
+        } else {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/io/IoReadFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/IoReadFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/io/IoReadFuture.java
index 3865ed4..a857d1c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/IoReadFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/IoReadFuture.java
@@ -18,32 +18,20 @@
  */
 package org.apache.sshd.common.io;
 
+import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.future.SshFuture;
 import org.apache.sshd.common.util.buffer.Buffer;
 
 public interface IoReadFuture extends SshFuture<IoReadFuture> {
-
     /**
      * Wait and verify that the read succeeded.
-     * @throws SshException if the action failed for any reason
-     */
-    void verify() throws SshException;
-
-
-    /**
-     * Wait and verify that the read succeeded within the specified timeout.
-     * @throws SshException if the action failed for any reason
-     */
-    void verify(long timeout, TimeUnit unit) throws SshException;
-
-    /**
-     * Wait and verify that the authentication succeeded within the specified timeout.
-     * @throws SshException if the action failed for any reason
+     * @throws IOException if the action failed for any reason
      */
-    void verify(long timeoutMillis) throws SshException;
+    void verify() throws IOException;
+    void verify(long timeout, TimeUnit unit) throws IOException;
+    void verify(long timeoutMillis) throws IOException;
 
     Buffer getBuffer();
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/io/IoWriteFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/IoWriteFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/io/IoWriteFuture.java
index 121d135..3483dff 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/IoWriteFuture.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/IoWriteFuture.java
@@ -18,17 +18,19 @@
  */
 package org.apache.sshd.common.io;
 
-import org.apache.sshd.common.SshException;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
 import org.apache.sshd.common.future.SshFuture;
 
 public interface IoWriteFuture extends SshFuture<IoWriteFuture> {
-
     /**
      * Wait and verify that the write succeeded.
-     *
-     * @throws SshException if the write failed for any reason
+     * @throws IOException if the write failed for any reason
      */
-    void verify() throws SshException;
+    void verify() throws IOException;
+    void verify(long count, TimeUnit unit) throws IOException;
+    void verify(long timeout) throws IOException;
 
     /**
      * Returns <tt>true</tt> if the write operation is finished successfully.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java b/sshd-core/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
index 2dae76c..4939540 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
@@ -25,13 +25,14 @@ import org.apache.mina.core.future.IoFuture;
 import org.apache.mina.core.future.IoFutureListener;
 import org.apache.mina.core.future.WriteFuture;
 import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.future.DefaultCloseFuture;
-import org.apache.sshd.common.future.DefaultSshFuture;
+import org.apache.sshd.common.io.AbstractIoWriteFuture;
 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.CloseableUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
 /**
@@ -115,46 +116,6 @@ public class MinaSession extends CloseableUtils.AbstractInnerCloseable implement
 
     @Override
     public IoWriteFuture write(Buffer buffer) {
-        class Future extends DefaultSshFuture<IoWriteFuture> implements IoWriteFuture {
-            Future(Object lock) {
-                super(lock);
-            }
-
-            @Override
-            public void verify() throws SshException {
-                try {
-                    await();
-                }
-                catch (InterruptedException e) {
-                    throw new SshException("Interrupted", e);
-                }
-                if (!isWritten()) {
-                    throw new SshException("Write failed", getException());
-                }
-            }
-
-            @Override
-            public boolean isWritten() {
-                return getValue() instanceof Boolean;
-            }
-
-            public void setWritten() {
-                setValue(Boolean.TRUE);
-            }
-
-            @Override
-            public Throwable getException() {
-                Object v = getValue();
-                return v instanceof Throwable ? (Throwable) v : null;
-            }
-
-            public void setException(Throwable exception) {
-                if (exception == null) {
-                    throw new IllegalArgumentException("exception");
-                }
-                setValue(exception);
-            }
-        }
         final Future future = new Future(null);
         session.write(MinaSupport.asIoBuffer(buffer)).addListener(new IoFutureListener<WriteFuture>() {
             @Override
@@ -169,6 +130,20 @@ public class MinaSession extends CloseableUtils.AbstractInnerCloseable implement
         return future;
     }
 
+    private static class Future extends AbstractIoWriteFuture {
+        Future(Object lock) {
+            super(lock);
+        }
+
+        public void setWritten() {
+            setValue(Boolean.TRUE);
+        }
+
+        public void setException(Throwable exception) {
+            setValue(ValidateUtils.checkNotNull(exception, "No exception specified", GenericUtils.EMPTY_OBJECT_ARRAY));
+        }
+    }
+
     @Override
     public IoService getService() {
         return service;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2DefaultIoWriteFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2DefaultIoWriteFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2DefaultIoWriteFuture.java
new file mode 100644
index 0000000..b0ccc0d
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2DefaultIoWriteFuture.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.common.io.nio2;
+
+import java.nio.ByteBuffer;
+
+import org.apache.sshd.common.io.AbstractIoWriteFuture;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class Nio2DefaultIoWriteFuture extends AbstractIoWriteFuture {
+    private final ByteBuffer buffer;
+
+    public Nio2DefaultIoWriteFuture(Object lock, ByteBuffer buffer) {
+        super(lock);
+        this.buffer = buffer;
+    }
+
+    public ByteBuffer getBuffer() {
+        return buffer;
+    }
+
+    public void setWritten() {
+        setValue(Boolean.TRUE);
+    }
+
+    public void setException(Throwable exception) {
+        setValue(ValidateUtils.checkNotNull(exception, "No exception specified", GenericUtils.EMPTY_OBJECT_ARRAY));
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Service.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Service.java b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Service.java
index e93bff0..05c7392 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Service.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Service.java
@@ -56,8 +56,8 @@ public abstract class Nio2Service extends CloseableUtils.AbstractInnerCloseable
 
     public void dispose() {
         try {
-            close(true).await();
-        } catch (InterruptedException e) {
+            close(true).await();    // TODO use verify+(configurable) timeout
+        } catch (IOException e) {
             log.debug("Exception caught while closing", e);
         }
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
index 350ac17..7e137a6 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
@@ -32,9 +32,7 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.FactoryManagerUtils;
-import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultSshFuture;
 import org.apache.sshd.common.io.IoHandler;
 import org.apache.sshd.common.io.IoService;
 import org.apache.sshd.common.io.IoSession;
@@ -59,8 +57,8 @@ public class Nio2Session extends CloseableUtils.AbstractCloseable implements IoS
     private final SocketAddress localAddress;
     private final SocketAddress remoteAddress;
     private final FactoryManager manager;
-    private final Queue<DefaultIoWriteFuture> writes = new LinkedTransferQueue<DefaultIoWriteFuture>();
-    private final AtomicReference<DefaultIoWriteFuture> currentWrite = new AtomicReference<DefaultIoWriteFuture>();
+    private final Queue<Nio2DefaultIoWriteFuture> writes = new LinkedTransferQueue<Nio2DefaultIoWriteFuture>();
+    private final AtomicReference<Nio2DefaultIoWriteFuture> currentWrite = new AtomicReference<Nio2DefaultIoWriteFuture>();
 
     public Nio2Session(Nio2Service service, FactoryManager manager, IoHandler handler, AsynchronousSocketChannel socket) throws IOException {
         this.service = service;
@@ -117,7 +115,7 @@ public class Nio2Session extends CloseableUtils.AbstractCloseable implements IoS
         }
 
         ByteBuffer buf = ByteBuffer.wrap(buffer.array(), buffer.rpos(), buffer.available());
-        final DefaultIoWriteFuture future = new DefaultIoWriteFuture(null, buf);
+        final Nio2DefaultIoWriteFuture future = new Nio2DefaultIoWriteFuture(null, buf);
         if (isClosing()) {
             Throwable exc = new ClosedChannelException();
             future.setException(exc);
@@ -153,7 +151,7 @@ public class Nio2Session extends CloseableUtils.AbstractCloseable implements IoS
     @Override
     protected void doCloseImmediately() {
         for (;;) {
-            DefaultIoWriteFuture future = writes.poll();
+            Nio2DefaultIoWriteFuture future = writes.poll();
             if (future != null) {
                 future.setException(new ClosedChannelException());
             } else {
@@ -250,16 +248,17 @@ public class Nio2Session extends CloseableUtils.AbstractCloseable implements IoS
 
     @SuppressWarnings("synthetic-access")
     private void startWriting() {
-        final DefaultIoWriteFuture future = writes.peek();
+        final Nio2DefaultIoWriteFuture future = writes.peek();
         if (future != null) {
             if (currentWrite.compareAndSet(null, future)) {
                 try {
-                    socket.write(future.buffer, null, new Nio2CompletionHandler<Integer, Object>() {
+                    final ByteBuffer buffer = future.getBuffer();
+                    socket.write(buffer, null, new Nio2CompletionHandler<Integer, Object>() {
                         @Override
                         protected void onCompleted(Integer result, Object attachment) {
-                            if (future.buffer.hasRemaining()) {
+                            if (buffer.hasRemaining()) {
                                 try {
-                                    socket.write(future.buffer, null, this);
+                                    socket.write(buffer, null, this);
                                 } catch (Throwable t) {
                                     log.debug("Exception caught while writing", t);
                                     future.setWritten();
@@ -293,45 +292,6 @@ public class Nio2Session extends CloseableUtils.AbstractCloseable implements IoS
         }
     }
 
-    static class DefaultIoWriteFuture extends DefaultSshFuture<IoWriteFuture> implements IoWriteFuture {
-        private final ByteBuffer buffer;
-        DefaultIoWriteFuture(Object lock, ByteBuffer buffer) {
-            super(lock);
-            this.buffer = buffer;
-        }
-        @Override
-        public void verify() throws SshException {
-            try {
-                await();
-            }
-            catch (InterruptedException e) {
-                throw new SshException("Interrupted", e);
-            }
-            if (!isWritten()) {
-                throw new SshException("Write failed", getException());
-            }
-        }
-
-        @Override
-        public boolean isWritten() {
-            return getValue() instanceof Boolean;
-        }
-        public void setWritten() {
-            setValue(Boolean.TRUE);
-        }
-        @Override
-        public Throwable getException() {
-            Object v = getValue();
-            return v instanceof Throwable ? (Throwable) v : null;
-        }
-        public void setException(Throwable exception) {
-            if (exception == null) {
-                throw new IllegalArgumentException("exception");
-            }
-            setValue(exception);
-        }
-    }
-
     @Override
     public String toString() {
         return getClass().getSimpleName() + "[local=" + localAddress + ", remote=" + remoteAddress + "]";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index 1874494..9a384f3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -1513,68 +1513,6 @@ public abstract class AbstractSession extends CloseableUtils.AbstractInnerClosea
         return idleTimeoutMs;
     }
 
-    /**
-     * Future holding a packet pending key exchange termination.
-     */
-    protected static class PendingWriteFuture extends DefaultSshFuture<IoWriteFuture>
-            implements IoWriteFuture, SshFutureListener<IoWriteFuture> {
-
-        private final Buffer buffer;
-
-        protected PendingWriteFuture(Buffer buffer) {
-            super(null);
-            this.buffer = buffer;
-        }
-
-        public Buffer getBuffer() {
-            return buffer;
-        }
-
-        @Override
-        public void verify() throws SshException {
-            try {
-                await();
-            }
-            catch (InterruptedException e) {
-                throw new SshException("Interrupted", e);
-            }
-            if (!isWritten()) {
-                throw new SshException("Write failed", getException());
-            }
-        }
-
-        @Override
-        public boolean isWritten() {
-            return getValue() instanceof Boolean;
-        }
-
-        @Override
-        public Throwable getException() {
-            Object v = getValue();
-            return v instanceof Throwable ? (Throwable) v : null;
-        }
-
-        public void setWritten() {
-            setValue(Boolean.TRUE);
-        }
-
-        public void setException(Throwable cause) {
-            if (cause == null) {
-                throw new IllegalArgumentException("No cause specified");
-            }
-            setValue(cause);
-        }
-
-        @Override
-        public void operationComplete(IoWriteFuture future) {
-            if (future.isWritten()) {
-                setWritten();
-            } else {
-                setException(future.getException());
-            }
-        }
-    }
-
     @Override
     public String toString() {
         return getClass().getSimpleName() + "[" + getUsername() + "@" + getIoSession().getRemoteAddress() + "]";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/session/PendingWriteFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/PendingWriteFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/session/PendingWriteFuture.java
new file mode 100644
index 0000000..a0c1430
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/PendingWriteFuture.java
@@ -0,0 +1,61 @@
+/*
+ * 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.session;
+
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.AbstractIoWriteFuture;
+import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * Future holding a packet pending key exchange termination.
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PendingWriteFuture extends AbstractIoWriteFuture implements SshFutureListener<IoWriteFuture> {
+    private final Buffer buffer;
+
+    public PendingWriteFuture(Buffer buffer) {
+        super(null);
+        this.buffer = ValidateUtils.checkNotNull(buffer, "No buffer provided", GenericUtils.EMPTY_OBJECT_ARRAY);
+    }
+
+    public Buffer getBuffer() {
+        return buffer;
+    }
+
+    public void setWritten() {
+        setValue(Boolean.TRUE);
+    }
+
+    public void setException(Throwable cause) {
+        ValidateUtils.checkNotNull(cause, "No cause specified", GenericUtils.EMPTY_OBJECT_ARRAY);
+        setValue(cause);
+    }
+
+    @Override
+    public void operationComplete(IoWriteFuture future) {
+        if (future.isWritten()) {
+            setWritten();
+        } else {
+            setException(future.getException());
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/util/CloseableUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/CloseableUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/CloseableUtils.java
index a75f470..dc566dc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/CloseableUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/CloseableUtils.java
@@ -19,7 +19,6 @@
 package org.apache.sshd.common.util;
 
 import java.io.IOException;
-import java.io.InterruptedIOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -56,11 +55,7 @@ public final class CloseableUtils {
 
         if ((!closeable.isClosed()) && (!closeable.isClosing())) {
             CloseFuture future=closeable.close(true);
-            try {
-                future.await();
-            } catch(InterruptedException e) {
-                throw new InterruptedIOException(e.getClass().getSimpleName() + " while await future closure: " + e.getMessage());
-            }
+            future.await();  // TODO use verify + configurable timeout
         }
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index a14bea8..2ca27d0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -361,6 +361,25 @@ public final class GenericUtils {
     }
     
     /**
+     * @param t The original {@link Throwable} - ignored if {@code null}
+     * @return If {@link Throwable#getCause()} is non-{@code null} then
+     * the cause, otherwise the original exception - {@code null} if
+     * the original exception was {@code null}
+     */
+    public static Throwable resolveExceptionCause(Throwable t) {
+        if (t == null) {
+            return t;
+        }
+        
+        Throwable c = t.getCause();
+        if (c == null) {
+            return t;
+        } else {
+            return c;
+        }
+    }
+
+    /**
      * Used to &quot;accumulate&quot; exceptions of the <U>same type</U>. If the
      * current exception is {@code null} then the new one becomes the current,
      * otherwise the new one is added as a <U>suppressed</U> exception to the

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
index 135ac0d..3cd5b04 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
@@ -287,12 +287,12 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
     /**
      * Stop the SSH server.  This method will block until all resources are actually disposed.
      */
-    public void stop() throws InterruptedException {
+    public void stop() throws IOException {
         stop(false);
     }
 
-    public void stop(boolean immediately) throws InterruptedException {
-        close(immediately).await();
+    public void stop(boolean immediately) throws IOException {
+        close(immediately).await(); // TODO use verify + configurable timeout
     }
 
     public void open() throws IOException {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/test/java/org/apache/sshd/AuthenticationTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/AuthenticationTest.java b/sshd-core/src/test/java/org/apache/sshd/AuthenticationTest.java
index 860ffed..4c1029a 100644
--- a/sshd-core/src/test/java/org/apache/sshd/AuthenticationTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/AuthenticationTest.java
@@ -21,6 +21,7 @@ package org.apache.sshd;
 import java.io.IOException;
 import java.security.KeyPair;
 import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.future.AuthFuture;
@@ -87,7 +88,7 @@ public class AuthenticationTest extends BaseTestSupport {
     public void testWrongPassword() throws Exception {
         try(SshClient client = SshClient.setUpDefaultClient()) {
             client.start();
-            try(ClientSession s = client.connect("user", "localhost", port).await().getSession()) {
+            try(ClientSession s = client.connect("user", "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
                 s.addPasswordIdentity("bad password");
                 assertTrue(s.auth().await().isFailure());
 
@@ -105,7 +106,7 @@ public class AuthenticationTest extends BaseTestSupport {
 
             client.start();
                 
-            try(ClientSession s = client.connect(null, "localhost", port).await().getSession()) {
+            try(ClientSession s = client.connect(null, "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
                 s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
         
                 assertFalse("Unexpected user1 password auth success", authPassword(s, "user1", "the-password").await().isSuccess());
@@ -129,7 +130,7 @@ public class AuthenticationTest extends BaseTestSupport {
             ));
             client.start();
             
-            try(ClientSession s = client.connect(null, "localhost", port).await().getSession()) {
+            try(ClientSession s = client.connect(null, "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
                 s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
         
                 assertFalse("Unexpected password auth sucess", authPassword(s, getCurrentTestName(), getCurrentTestName()).await().isSuccess());
@@ -150,7 +151,7 @@ public class AuthenticationTest extends BaseTestSupport {
             ));
             client.start();
             
-            try(ClientSession s = client.connect(null, "localhost", port).await().getSession()) {
+            try(ClientSession s = client.connect(null, "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
                 s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
         
                 KeyPair pair = Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);
@@ -172,7 +173,7 @@ public class AuthenticationTest extends BaseTestSupport {
             ));
             client.start();
             
-            try(ClientSession s = client.connect(null, "localhost", port).await().getSession()) {
+            try(ClientSession s = client.connect(null, "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
                 s.waitFor(ClientSession.CLOSED | ClientSession.WAIT_AUTH, 0);
         
                 KeyPair pair = Utils.createTestHostKeyProvider().loadKey(KeyPairProvider.SSH_RSA);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java b/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
index 671a341..edb1047 100644
--- a/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
@@ -80,7 +80,7 @@ public class KeepAliveTest extends BaseTestSupport {
         SshClient client = SshClient.setUpDefaultClient();
         client.start();
         
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
             
@@ -100,7 +100,7 @@ public class KeepAliveTest extends BaseTestSupport {
         SshClient client = SshClient.setUpDefaultClient();
         client.start();
         
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
         
@@ -121,7 +121,7 @@ public class KeepAliveTest extends BaseTestSupport {
         FactoryManagerUtils.updateProperty(client, ClientFactoryManager.HEARTBEAT_INTERVAL, HEARTBEAT);
         client.start();
 
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
 
@@ -142,7 +142,7 @@ public class KeepAliveTest extends BaseTestSupport {
         FactoryManagerUtils.updateProperty(client, ClientFactoryManager.HEARTBEAT_INTERVAL, HEARTBEAT);
         client.start();
 
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
             
@@ -164,7 +164,7 @@ public class KeepAliveTest extends BaseTestSupport {
         SshClient client = SshClient.setUpDefaultClient();
         client.start();
         
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
 
@@ -194,7 +194,7 @@ public class KeepAliveTest extends BaseTestSupport {
         SshClient client = SshClient.setUpDefaultClient();
         client.start();
 
-        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+        try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
             session.addPasswordIdentity(getCurrentTestName());
             session.auth().verify(5L, TimeUnit.SECONDS);
             

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
index 853e16b..de75221 100644
--- a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
@@ -125,7 +125,7 @@ public class KeyReExchangeTest extends BaseTestSupport {
         try(SshClient client = SshClient.setUpDefaultClient()) {
             client.start();
         
-            try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
                 session.addPasswordIdentity(getCurrentTestName());
                 session.auth().verify(5L, TimeUnit.SECONDS);
                 
@@ -179,7 +179,7 @@ public class KeyReExchangeTest extends BaseTestSupport {
         try(SshClient client = SshClient.setUpDefaultClient()) {
             client.start();
             
-            try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
                 session.addPasswordIdentity(getCurrentTestName());
                 session.auth().verify(5L, TimeUnit.SECONDS);
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/test/java/org/apache/sshd/LoadTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/LoadTest.java b/sshd-core/src/test/java/org/apache/sshd/LoadTest.java
index 656b871..26a6b2a 100644
--- a/sshd-core/src/test/java/org/apache/sshd/LoadTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/LoadTest.java
@@ -127,7 +127,7 @@ public class LoadTest extends BaseTestSupport {
                     ClientBuilder.DH2KEX.transform(BuiltinDHFactories.dhg1)));
             client.setCipherFactories(Arrays.<NamedFactory<Cipher>>asList(BuiltinCiphers.blowfishcbc));
             client.start();
-            try(ClientSession session = client.connect("sshd", "localhost", port).await().getSession()) {
+            try(ClientSession session = client.connect("sshd", "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
                 session.addPasswordIdentity("sshd");
                 session.auth().verify(5L, TimeUnit.SECONDS);
     

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/test/java/org/apache/sshd/PortForwardingTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/PortForwardingTest.java b/sshd-core/src/test/java/org/apache/sshd/PortForwardingTest.java
index d9e966a..65bd93f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/PortForwardingTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/PortForwardingTest.java
@@ -516,7 +516,7 @@ public class PortForwardingTest extends BaseTestSupport {
         client.setTcpipForwardingFilter(ForwardingFilter.AcceptAllForwardingFilter.INSTANCE);
         client.start();
 
-        ClientSession session = client.connect("sshd", "localhost", sshPort).await().getSession();
+        ClientSession session = client.connect("sshd", "localhost", sshPort).verify(7L, TimeUnit.SECONDS).getSession();
         session.addPasswordIdentity("sshd");
         session.auth().verify();
         return session;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java b/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
index a0dd31a..a050b80 100644
--- a/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
@@ -150,7 +150,7 @@ public class ProxyTest extends BaseTestSupport {
         client.setTcpipForwardingFilter(ForwardingFilter.AcceptAllForwardingFilter.INSTANCE);
         client.start();
 
-        ClientSession session = client.connect("sshd", "localhost", sshPort).await().getSession();
+        ClientSession session = client.connect("sshd", "localhost", sshPort).verify(7L, TimeUnit.SECONDS).getSession();
         session.addPasswordIdentity("sshd");
         session.auth().verify();
         return session;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/8a16e8cc/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java b/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
index 3cabd21..1a5fa21 100644
--- a/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/SinglePublicKeyAuthTest.java
@@ -22,6 +22,7 @@ import java.security.KeyPair;
 import java.security.PublicKey;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.sshd.client.SshClient;
@@ -113,7 +114,7 @@ public class SinglePublicKeyAuthTest extends BaseTestSupport {
         try(SshClient client = SshClient.setUpDefaultClient()) {
             client.start();
             
-            try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
                 session.addPublicKeyIdentity(pairRsaBad);
                 session.addPublicKeyIdentity(pairRsa);
                 assertTrue(session.auth().await().isSuccess());
@@ -148,7 +149,7 @@ public class SinglePublicKeyAuthTest extends BaseTestSupport {
         try(SshClient client = SshClient.setUpDefaultClient()) {
             client.start();
             
-            try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).await().getSession()) {
+            try(ClientSession session = client.connect(getCurrentTestName(), "localhost", port).verify(7L, TimeUnit.SECONDS).getSession()) {
                 session.addPublicKeyIdentity(pairRsaBad);
                 session.addPublicKeyIdentity(pairRsa);
                 assertTrue("Failed to authenticate", session.auth().await().isSuccess());