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());
+                }
+            }
+        }
+    }
 }

Reply via email to