Repository: mina-sshd Updated Branches: refs/heads/master c7f3a3d83 -> 0cf8186ad
[SSHD-340] Mark SSH client/server as having 'Opened' state if re-started after being stopped Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/738264f4 Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/738264f4 Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/738264f4 Branch: refs/heads/master Commit: 738264f423540c095bfb919d4e066721762e00ca Parents: bd8fe7e Author: Goldstein Lyor <[email protected]> Authored: Wed Jul 25 09:04:11 2018 +0300 Committer: Goldstein Lyor <[email protected]> Committed: Wed Jul 25 09:04:40 2018 +0300 ---------------------------------------------------------------------- .../java/org/apache/sshd/client/SshClient.java | 8 ++++- .../org/apache/sshd/common/io/IoAcceptor.java | 32 ++++++++++++++++++++ .../util/closeable/AbstractCloseable.java | 17 +++++++++++ .../java/org/apache/sshd/server/SshServer.java | 20 ++++++++---- .../java/org/apache/sshd/client/ClientTest.java | 26 ++++++++++++++++ .../org/apache/sshd/server/SshServerTest.java | 27 ++++++++++++++++- 6 files changed, 122 insertions(+), 8 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/738264f4/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 143e4d7..dc4cfdf 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 @@ -72,6 +72,7 @@ import org.apache.sshd.common.future.SshFutureListener; import org.apache.sshd.common.helpers.AbstractFactoryManager; import org.apache.sshd.common.io.IoConnectFuture; import org.apache.sshd.common.io.IoConnector; +import org.apache.sshd.common.io.IoServiceFactory; import org.apache.sshd.common.io.IoSession; import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.session.helpers.AbstractSession; @@ -371,6 +372,9 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa */ public void start() { if (isStarted()) { + if (log.isDebugEnabled()) { + log.debug("start({}) already started", this); + } return; } @@ -380,6 +384,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa } setupSessionTimeout(sessionFactory); + reopenIfNotOpen(); connector = createConnector(); started.set(true); @@ -687,7 +692,8 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa } protected IoConnector createConnector() { - return getIoServiceFactory().createConnector(getSessionFactory()); + IoServiceFactory factory = getIoServiceFactory(); + return factory.createConnector(getSessionFactory()); } protected SessionFactory createSessionFactory() { http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/738264f4/sshd-core/src/main/java/org/apache/sshd/common/io/IoAcceptor.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/IoAcceptor.java b/sshd-core/src/main/java/org/apache/sshd/common/io/IoAcceptor.java index a86b73e..8ea353d 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/io/IoAcceptor.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/io/IoAcceptor.java @@ -19,10 +19,15 @@ package org.apache.sshd.common.io; import java.io.IOException; +import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; import java.util.Set; +import org.apache.sshd.common.util.GenericUtils; + /** * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> */ @@ -41,4 +46,31 @@ public interface IoAcceptor extends IoService { Set<SocketAddress> getBoundAddresses(); + /** + * @param acceptor The {@link IoAcceptor} - ignored if {@code null} + * @return The port associated with the <u>first</u> bound address - {@code -1} if none available + * @see #resolveBoundAddress(IoAcceptor) + */ + static int resolveBoundPort(IoAcceptor acceptor) { + SocketAddress boundEndpoint = resolveBoundAddress(acceptor); + if (boundEndpoint instanceof InetSocketAddress) { + return ((InetSocketAddress) boundEndpoint).getPort(); + } + + return -1; + } + + /** + * @param acceptor The {@link IoAcceptor} - ignored if {@code null} + * @return The <u>first</u> bound address - {@code null} if none available + * @see #getBoundAddresses() + */ + static SocketAddress resolveBoundAddress(IoAcceptor acceptor) { + Collection<SocketAddress> boundAddresses = (acceptor == null) ? Collections.emptySet() : acceptor.getBoundAddresses(); + if (GenericUtils.isEmpty(boundAddresses)) { + return null; + } + Iterator<SocketAddress> boundIterator = boundAddresses.iterator(); + return boundIterator.next(); + } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/738264f4/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java index dffbd5b..dc1838f 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java @@ -145,6 +145,23 @@ public abstract class AbstractCloseable extends IoBaseCloseable { } /** + * Checks if the current state is {@link State#Opened} and sets it + * as such if not. + * + * @return {@code true} if had to set the state to {@link State#Opened} + */ + protected boolean reopenIfNotOpen() { + State curState = state.get(); + if (curState == State.Opened) { + return false; + } + + log.info("reopenIfNotOpen({}) update state {} -> {}", this, curState, State.Opened); + state.set(State.Opened); + return true; + } + + /** * <P>doCloseImmediately is called once and only once * with state == Immediate</P> * http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/738264f4/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 35b72cc..0cd139e 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 @@ -278,7 +278,11 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa * @throws IOException If failed to start */ public void start() throws IOException { + boolean debugEnabled = log.isDebugEnabled(); if (isStarted()) { + if (debugEnabled) { + log.debug("start({}) already started", this); + } return; } @@ -289,33 +293,37 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa acceptor = createAcceptor(); setupSessionTimeout(sessionFactory); + reopenIfNotOpen(); String hostsList = getHost(); if (!GenericUtils.isEmpty(hostsList)) { String[] hosts = GenericUtils.split(hostsList, ','); + boolean traceEnabled = log.isTraceEnabled(); for (String host : hosts) { - if (log.isDebugEnabled()) { + if (debugEnabled) { log.debug("start() - resolve bind host={}", host); } InetAddress[] inetAddresses = InetAddress.getAllByName(host); for (InetAddress inetAddress : inetAddresses) { - if (log.isTraceEnabled()) { + if (traceEnabled) { log.trace("start() - bind host={} / {}", host, inetAddress); } acceptor.bind(new InetSocketAddress(inetAddress, port)); if (port == 0) { - port = ((InetSocketAddress) acceptor.getBoundAddresses().iterator().next()).getPort(); - log.info("start() listen on auto-allocated port=" + port); + port = IoAcceptor.resolveBoundPort(acceptor); + ValidateUtils.checkState(port > 0, "Cannot resolve bound port for host=%s", host); + log.info("start() listen on auto-allocated port={} for host={}[{}]", port, host, inetAddress); } } } } else { acceptor.bind(new InetSocketAddress(port)); if (port == 0) { - port = ((InetSocketAddress) acceptor.getBoundAddresses().iterator().next()).getPort(); - log.info("start() listen on auto-allocated port=" + port); + port = IoAcceptor.resolveBoundPort(acceptor); + ValidateUtils.checkState(port > 0, "Cannot resolve generic bound port"); + log.info("start({}) listen on auto-allocated port={}", this, port); } } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/738264f4/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java index 43d6d33..4589b48 100644 --- a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java @@ -245,6 +245,32 @@ public class ClientTest extends BaseTestSupport { } @Test + public void testClientStopIsIdempotent() throws Exception { + client.start(); + for (int index = 1; index <= 4; index++) { + client.stop(); + } + } + + @Test // see SSHD-340 + public void testClientIsRestartable() throws Exception { + for (int index = 1; index <= 4; index++) { + client.start(); + assertTrue("Client not started at attempt #" + index, client.isStarted()); + assertTrue("Client not open at attempt #" + index, client.isOpen()); + + try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + s.addPasswordIdentity(getCurrentTestName()); + s.auth().verify(11L, TimeUnit.SECONDS); + } catch (Exception e) { + fail("Failed (" + e.getClass().getSimpleName() + ") to authenticate at attempt #" + index + ": " + e.getMessage()); + } + + client.stop(); + } + } + + @Test public void testPropertyResolutionHierarchy() throws Exception { String sessionPropName = getCurrentTestName() + "-session"; AtomicReference<Object> sessionConfigValueHolder = new AtomicReference<>(null); http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/738264f4/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java index a683afb..43cadec 100644 --- a/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/server/SshServerTest.java @@ -20,7 +20,10 @@ package org.apache.sshd.server; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import org.apache.sshd.client.SshClient; +import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.util.test.BaseTestSupport; import org.junit.FixMethodOrder; import org.junit.Test; @@ -81,8 +84,30 @@ public class SshServerTest extends BaseTestSupport { sshd.start(); assertNotEquals(0, sshd.getPort()); - sshd.stop(); } } + + @Test // see SSHD-340 + public void testServerRestartable() throws Exception { + try (SshClient client = setupTestClient(); + SshServer sshd = setupTestServer()) { + sshd.setHost(TEST_LOCALHOST); + client.start(); + + for (int index = 1; index <= 4; index++) { + sshd.start(); + assertTrue("SSHD not started at attempt #" + index, sshd.isStarted()); + assertTrue("SSHD not open at attempt #" + index, sshd.isOpen()); + + int port = sshd.getPort(); + try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + s.addPasswordIdentity(getCurrentTestName()); + s.auth().verify(11L, TimeUnit.SECONDS); + } catch (Exception e) { + fail("Failed (" + e.getClass().getSimpleName() + ") to authenticate at attempt #" + index + ": " + e.getMessage()); + } + } + } + } }
