Repository: mina-sshd Updated Branches: refs/heads/master 6a8253d62 -> d8c861f2d
Add a configurable wait time for closing or stopping Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/d8c861f2 Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/d8c861f2 Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/d8c861f2 Branch: refs/heads/master Commit: d8c861f2d282648ea490cd2bda580cc619205a30 Parents: 6a8253d Author: Lyor Goldstein <[email protected]> Authored: Sun Jan 24 08:23:42 2016 +0200 Committer: Lyor Goldstein <[email protected]> Committed: Sun Jan 24 08:23:42 2016 +0200 ---------------------------------------------------------------------- .../java/org/apache/sshd/client/SshClient.java | 15 ++++++++++-- .../org/apache/sshd/common/FactoryManager.java | 12 ++++++++++ .../apache/sshd/common/io/nio2/Nio2Service.java | 20 +++++++++++++--- .../common/util/closeable/CloseableUtils.java | 25 +++++++++++++++++++- .../java/org/apache/sshd/server/SshServer.java | 7 +++++- 5 files changed, 72 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d8c861f2/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 8c31468..92bfbba 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 @@ -30,6 +30,7 @@ import java.io.StreamCorruptedException; import java.io.StringWriter; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.net.SocketTimeoutException; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; @@ -396,9 +397,19 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa public void stop() { try { - close(true).await(); // TODO use verify + configurable timeout + long maxWait = PropertyResolverUtils.getLongProperty(this, STOP_WAIT_TIME, DEFAULT_STOP_WAIT_TIME); + boolean successful = close(true).await(maxWait); + if (!successful) { + throw new SocketTimeoutException("Failed to receive closure confirmation within " + maxWait + " millis"); + } } catch (IOException e) { - log.debug("Exception caught while stopping client", e); + if (log.isDebugEnabled()) { + log.debug(e.getClass().getSimpleName() + " while stopping client: " + e.getMessage()); + } + + if (log.isTraceEnabled()) { + log.trace("Stop exception details", e); + } } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d8c861f2/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java index eadc6cb..9a3a65c 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java @@ -144,6 +144,18 @@ public interface FactoryManager extends KexFactoryManager, SessionListenerManage long DEFAULT_CHANNEL_CLOSE_TIMEOUT = TimeUnit.SECONDS.toMillis(5L); /** + * Timeout (milliseconds) to wait for client / server stop request + * if immediate stop requested. + * @see #DEFAULT_STOP_WAIT_TIME + */ + String STOP_WAIT_TIME = "stop-wait-time"; + + /** + * Default value for {@link #STOP_WAIT_TIME} if none specified + */ + long DEFAULT_STOP_WAIT_TIME = TimeUnit.MINUTES.toMillis(1L); + + /** * Socket backlog. * See {@link java.nio.channels.AsynchronousServerSocketChannel#bind(java.net.SocketAddress, int)} */ http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d8c861f2/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 ba1494c..1df3731 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 @@ -20,6 +20,7 @@ package org.apache.sshd.common.io.nio2; import java.io.IOException; import java.net.SocketOption; +import java.net.SocketTimeoutException; import java.nio.channels.AsynchronousChannelGroup; import java.nio.channels.NetworkChannel; import java.util.Collection; @@ -37,6 +38,7 @@ import org.apache.sshd.common.io.IoSession; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.closeable.AbstractInnerCloseable; +import org.apache.sshd.common.util.closeable.CloseableUtils; /** */ @@ -48,7 +50,9 @@ public abstract class Nio2Service extends AbstractInnerCloseable implements IoSe protected final AsynchronousChannelGroup group; protected Nio2Service(FactoryManager manager, IoHandler handler, AsynchronousChannelGroup group) { - log.trace("Creating {}", getClass().getSimpleName()); + if (log.isTraceEnabled()) { + log.trace("Creating {}", getClass().getSimpleName()); + } this.manager = ValidateUtils.checkNotNull(manager, "No factory manager provided"); this.handler = ValidateUtils.checkNotNull(handler, "No I/O handler provided"); this.group = ValidateUtils.checkNotNull(group, "No async. channel group provided"); @@ -57,9 +61,19 @@ public abstract class Nio2Service extends AbstractInnerCloseable implements IoSe public void dispose() { try { - close(true).await(); // TODO use verify+(configurable) timeout + long maxWait = CloseableUtils.getMaxCloseWaitTime(manager); + boolean successful = close(true).await(maxWait); + if (!successful) { + throw new SocketTimeoutException("Failed to receive closure confirmation within " + maxWait + " millis"); + } } catch (IOException e) { - log.debug("Exception caught while closing", e); + if (log.isDebugEnabled()) { + log.debug(e.getClass().getSimpleName() + " while stopping service: " + e.getMessage()); + } + + if (log.isTraceEnabled()) { + log.trace("Stop exception details", e); + } } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d8c861f2/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/CloseableUtils.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/CloseableUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/CloseableUtils.java index 172ce99..e0223c1 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/CloseableUtils.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/CloseableUtils.java @@ -19,8 +19,12 @@ package org.apache.sshd.common.util.closeable; import java.io.IOException; +import java.net.SocketTimeoutException; +import java.util.concurrent.TimeUnit; import org.apache.sshd.common.Closeable; +import org.apache.sshd.common.PropertyResolver; +import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.future.CloseFuture; import org.apache.sshd.common.future.DefaultCloseFuture; @@ -30,6 +34,17 @@ import org.apache.sshd.common.future.DefaultCloseFuture; * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> */ public final class CloseableUtils { + /** + * Timeout (milliseconds) for waiting on a {@link CloseFuture} to successfully + * complete its action. + * @see #DEFAULT_CLOSE_WAIT_TIMEOUT + */ + public static final String CLOSE_WAIT_TIMEOUT = "sshd-close-wait-time"; + + /** + * Default value for {@link #CLOSE_WAIT_TIMEOUT} if none specified + */ + public static final long DEFAULT_CLOSE_WAIT_TIMEOUT = TimeUnit.SECONDS.toMillis(15L); /** * Private Constructor @@ -38,6 +53,10 @@ public final class CloseableUtils { throw new UnsupportedOperationException("No instance allowed"); } + public static long getMaxCloseWaitTime(PropertyResolver resolver) { + return (resolver == null) ? DEFAULT_CLOSE_WAIT_TIMEOUT : PropertyResolverUtils.getLongProperty(resolver, CLOSE_WAIT_TIMEOUT, DEFAULT_CLOSE_WAIT_TIMEOUT); + } + // TODO once JDK 8+ becomes the minimum for this project, make it a static method in the Closeable interface public static void close(Closeable closeable) throws IOException { if (closeable == null) { @@ -46,7 +65,11 @@ public final class CloseableUtils { if ((!closeable.isClosed()) && (!closeable.isClosing())) { CloseFuture future = closeable.close(true); - future.await(); // TODO use verify + configurable timeout + long maxWait = (closeable instanceof PropertyResolver) ? getMaxCloseWaitTime((PropertyResolver) closeable) : DEFAULT_CLOSE_WAIT_TIMEOUT; + boolean successful = future.await(maxWait); + if (!successful) { + throw new SocketTimeoutException("Failed to receive closure confirmation within " + maxWait + " millis"); + } } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d8c861f2/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 2c63f26..fa573fb 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 @@ -22,6 +22,7 @@ import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -316,7 +317,11 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa } public void stop(boolean immediately) throws IOException { - close(immediately).await(); // TODO use verify + configurable timeout + long maxWait = immediately ? PropertyResolverUtils.getLongProperty(this, STOP_WAIT_TIME, DEFAULT_STOP_WAIT_TIME) : Long.MAX_VALUE; + boolean successful = close(immediately).await(maxWait); + if (!successful) { + throw new SocketTimeoutException("Failed to receive closure confirmation within " + maxWait + " millis"); + } } public void open() throws IOException {
