Repository: mina-sshd
Updated Branches:
  refs/heads/master cbb92d2a8 -> be51bdc8a


[SSHD-732] ClientIdentitiesWatcher should load keys one at a time

Make sure iterators are lazy, i.e. they don't call hasNext() or next() unless 
actually required.  Use the Stream api in some cases to simplify the code.

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

Branch: refs/heads/master
Commit: 612218b7739435e2596fa47d010f3628d91569e4
Parents: cbb92d2
Author: Guillaume Nodet <gno...@apache.org>
Authored: Fri Mar 10 11:58:37 2017 +0100
Committer: Guillaume Nodet <gno...@apache.org>
Committed: Fri Mar 10 11:58:37 2017 +0100

----------------------------------------------------------------------
 .../auth/hostbased/HostKeyIdentityProvider.java |  5 +-
 .../auth/pubkey/UserAuthPublicKeyIterator.java  | 36 ++++--------
 .../keys/BuiltinClientIdentitiesWatcher.java    | 38 ++++---------
 .../config/keys/ClientIdentitiesWatcher.java    | 59 +++++++++++++-------
 .../common/keyprovider/KeyIdentityProvider.java | 20 ++++++-
 .../apache/sshd/common/util/GenericUtils.java   | 45 +++------------
 6 files changed, 90 insertions(+), 113 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/612218b7/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
 
b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
index 62206a1..b141eb5 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
@@ -47,9 +47,6 @@ public interface HostKeyIdentityProvider {
     }
 
     static HostKeyIdentityProvider wrap(Iterable<? extends KeyPair> pairs) {
-        return () -> () -> {
-            Iterator<? extends KeyPair> iter = GenericUtils.iteratorOf(pairs);
-            return GenericUtils.wrapIterator(iter, kp -> new Pair<>(kp, 
Collections.<X509Certificate>emptyList()));
-        };
+        return () -> GenericUtils.wrapIterable(pairs, kp -> new Pair<>(kp, 
Collections.<X509Certificate>emptyList()));
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/612218b7/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
 
b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
index 0ecb740..e1c7c86 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
@@ -27,6 +27,7 @@ import java.util.LinkedList;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Stream;
 
 import org.apache.sshd.agent.SshAgent;
 import org.apache.sshd.agent.SshAgentFactory;
@@ -42,22 +43,21 @@ import org.apache.sshd.common.util.GenericUtils;
 public class UserAuthPublicKeyIterator extends 
AbstractKeyPairIterator<PublicKeyIdentity> implements Channel {
 
     private final AtomicBoolean open = new AtomicBoolean(true);
-    private final Iterator<Iterator<? extends PublicKeyIdentity>> iterators;
     private Iterator<? extends PublicKeyIdentity> current;
     private SshAgent agent;
 
     public UserAuthPublicKeyIterator(ClientSession session, 
SignatureFactoriesManager signatureFactories) throws Exception {
         super(session);
 
-        Collection<Iterator<? extends PublicKeyIdentity>> identities = new 
LinkedList<>();
-        identities.add(new SessionKeyPairIterator(session, signatureFactories, 
KeyIdentityProvider.iteratorOf(session)));
+        Collection<Stream<? extends PublicKeyIdentity>> identities = new 
LinkedList<>();
 
         FactoryManager manager = 
Objects.requireNonNull(session.getFactoryManager(), "No session factory 
manager");
         SshAgentFactory factory = manager.getAgentFactory();
         if (factory != null) {
             try {
                 agent = Objects.requireNonNull(factory.createClient(manager), 
"No agent created");
-                identities.add(new SshAgentPublicKeyIterator(session, agent));
+                identities.add(agent.getIdentities().stream()
+                        .map(kp -> new KeyAgentIdentity(agent, kp.getFirst(), 
kp.getSecond())));
             } catch (Exception e) {
                 try {
                     closeAgent();
@@ -69,8 +69,12 @@ public class UserAuthPublicKeyIterator extends 
AbstractKeyPairIterator<PublicKey
             }
         }
 
-        iterators = GenericUtils.iteratorOf(identities);
-        current = nextIterator(iterators);
+        identities.add(Stream.of(KeyIdentityProvider.providerOf(session))
+                .map(KeyIdentityProvider::loadKeys)
+                .flatMap(GenericUtils::stream)
+                .map(kp -> new KeyPairIdentity(session, signatureFactories, 
kp)));
+
+        current = identities.stream().flatMap(r -> r).iterator();
     }
 
     @Override
@@ -79,7 +83,7 @@ public class UserAuthPublicKeyIterator extends 
AbstractKeyPairIterator<PublicKey
             return false;
         }
 
-        return current != null;
+        return current.hasNext();
     }
 
     @Override
@@ -87,12 +91,7 @@ public class UserAuthPublicKeyIterator extends 
AbstractKeyPairIterator<PublicKey
         if (!isOpen()) {
             throw new NoSuchElementException("Iterator is closed");
         }
-
-        PublicKeyIdentity pki = current.next();
-        if (!current.hasNext()) {
-            current = nextIterator(iterators);
-        }
-        return pki;
+        return current.next();
     }
 
     @Override
@@ -117,15 +116,4 @@ public class UserAuthPublicKeyIterator extends 
AbstractKeyPairIterator<PublicKey
         }
     }
 
-    protected Iterator<? extends PublicKeyIdentity> nextIterator(
-            Iterator<? extends Iterator<? extends PublicKeyIdentity>> 
available) {
-        while ((available != null) && available.hasNext()) {
-            Iterator<? extends PublicKeyIdentity> iter = available.next();
-            if (iter.hasNext()) {
-                return iter;
-            }
-        }
-
-        return null;
-    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/612218b7/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
 
b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
index e2cc319..ab9929b 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
@@ -24,7 +24,6 @@ import java.security.KeyPair;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Supplier;
@@ -70,33 +69,20 @@ public class BuiltinClientIdentitiesWatcher extends 
ClientIdentitiesWatcher {
     }
 
     @Override
-    public List<KeyPair> loadKeys() {
-        List<KeyPair> keys = super.loadKeys();
-        if (GenericUtils.isEmpty(keys) || (!isSupportedOnly())) {
-            return keys;
-        }
-
-        Collection<KeyPair> toRemove = null;
-        for (KeyPair kp : keys) {
-            BuiltinIdentities id = BuiltinIdentities.fromKeyPair(kp);
-            if ((id != null) && id.isSupported()) {
-                continue;
-            }
-
-            if (log.isDebugEnabled()) {
-                log.debug("loadKeys - remove unsupported identity={}, 
key-type={}, key={}",
-                          id, KeyUtils.getKeyType(kp), 
KeyUtils.getFingerPrint(kp.getPublic()));
-            }
+    public Iterable<KeyPair> loadKeys() {
+        return isSupportedOnly() ? loadKeys(this::isSupported) : 
super.loadKeys();
+    }
 
-            if (toRemove == null) {
-                toRemove = new LinkedList<>();
-            }
-            toRemove.add(kp);
+    private boolean isSupported(KeyPair kp) {
+        BuiltinIdentities id = BuiltinIdentities.fromKeyPair(kp);
+        if ((id != null) && id.isSupported()) {
+            return true;
         }
-
-        GenericUtils.forEach(toRemove, keys::remove);
-
-        return keys;
+        if (log.isDebugEnabled()) {
+            log.debug("loadKeys - remove unsupported identity={}, key-type={}, 
key={}",
+                    id, KeyUtils.getKeyType(kp), 
KeyUtils.getFingerPrint(kp.getPublic()));
+        }
+        return false;
     }
 
     public static List<Path> getDefaultBuiltinIdentitiesPaths(Path keysFolder) 
{

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/612218b7/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
 
b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
index 3a78958..6c2bb0b 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
@@ -21,12 +21,15 @@ package org.apache.sshd.client.config.keys;
 
 import java.nio.file.Path;
 import java.security.KeyPair;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
 import java.util.function.Supplier;
+import java.util.stream.Stream;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
@@ -69,32 +72,46 @@ public class ClientIdentitiesWatcher extends 
AbstractKeyPairProvider implements
     }
 
     @Override
-    public List<KeyPair> loadKeys() {
-        if (GenericUtils.isEmpty(providers)) {
-            return Collections.emptyList();
-        }
+    public Iterable<KeyPair> loadKeys() {
+        return loadKeys(null);
+    }
 
-        List<KeyPair> keys = new ArrayList<>(providers.size()); // optimistic 
initialization
-        for (ClientIdentityProvider p : providers) {
-            try {
-                KeyPair kp = p.getClientIdentity();
-                if (kp == null) {
-                    if (log.isDebugEnabled()) {
-                        log.debug("loadKeys({}) no key loaded", p);
-                    }
-                    continue;
-                }
+    protected Iterable<KeyPair> loadKeys(Predicate<KeyPair> filter) {
+        return () -> {
+            Stream<KeyPair> stream = safeMap(GenericUtils.stream(providers), 
this::doGetKeyPair);
+            if (filter != null) {
+                stream = stream.filter(filter);
+            }
+            return stream.iterator();
+        };
+    }
+
+    /**
+     * Performs a mapping operation on the stream, discarding any null values
+     * returned by the mapper.
+     */
+    private <U, V> Stream<V> safeMap(Stream<U> stream, Function<U, V> mapper) {
+        return stream.map(u -> Optional.ofNullable(mapper.apply(u)))
+                .filter(Optional::isPresent)
+                .map(Optional::get);
+    }
 
-                keys.add(kp);
-            } catch (Throwable e) {
-                log.warn("loadKeys({}) failed ({}) to load key: {}", p, 
e.getClass().getSimpleName(), e.getMessage());
+    private KeyPair doGetKeyPair(ClientIdentityProvider p) {
+        try {
+            KeyPair kp = p.getClientIdentity();
+            if (kp == null) {
                 if (log.isDebugEnabled()) {
-                    log.debug("loadKeys(" + p + ") key load failure details", 
e);
+                    log.debug("loadKeys({}) no key loaded", p);
                 }
             }
+            return kp;
+        } catch (Throwable e) {
+            log.warn("loadKeys({}) failed ({}) to load key: {}", p, 
e.getClass().getSimpleName(), e.getMessage());
+            if (log.isDebugEnabled()) {
+                log.debug("loadKeys(" + p + ") key load failure details", e);
+            }
+            return null;
         }
-
-        return keys;
     }
 
     public static List<ClientIdentityProvider> buildProviders(

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/612218b7/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
index c8cddfa..626c706 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
@@ -65,6 +65,24 @@ public interface KeyIdentityProvider {
     Iterable<KeyPair> loadKeys();
 
     /**
+     * Creates a &quot;unified&quot; {@link KeyIdentityProvider} of key pairs 
out of the registered
+     * {@link KeyPair} identities and the extra available ones as a single 
iterator
+     * of key pairs
+     *
+     *
+     * @param session The {@link ClientSession} - ignored if {@code null} 
(i.e., empty
+     * iterator returned)
+     * @return The wrapping KeyIdentityProvider
+     * @see ClientSession#getRegisteredIdentities()
+     * @see ClientSession#getKeyPairProvider()
+     */
+    static KeyIdentityProvider providerOf(ClientSession session) {
+        return session == null
+                ? EMPTY_KEYS_PROVIDER
+                : 
resolveKeyIdentityProvider(session.getRegisteredIdentities(), 
session.getKeyPairProvider());
+    }
+
+    /**
      * Creates a &quot;unified&quot; {@link Iterator} of key pairs out of the 
registered
      * {@link KeyPair} identities and the extra available ones as a single 
iterator
      * of key pairs
@@ -76,7 +94,7 @@ public interface KeyIdentityProvider {
      * @see ClientSession#getKeyPairProvider()
      */
     static Iterator<KeyPair> iteratorOf(ClientSession session) {
-        return (session == null) ? Collections.<KeyPair>emptyIterator() : 
iteratorOf(session.getRegisteredIdentities(), session.getKeyPairProvider());
+        return iteratorOf(providerOf(session));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/612218b7/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 82d78d1..e2f8733 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
@@ -33,7 +33,6 @@ import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
 import java.util.SortedMap;
@@ -756,7 +755,13 @@ public final class GenericUtils {
     }
 
     public static <U, V> Iterable<V> wrapIterable(Iterable<? extends U> iter, 
Function<U, V> mapper) {
-        return () -> wrapIterator(iteratorOf(iter), mapper);
+        return () -> wrapIterator(iter, mapper);
+    }
+
+    public static <U, V> Iterator<V> wrapIterator(Iterable<? extends U> iter, 
Function<U, V> mapper) {
+        return stream(iter)
+                .map(mapper::apply)
+                .iterator();
     }
 
     public static <U, V> Iterator<V> wrapIterator(Iterator<? extends U> iter, 
Function<U, V> mapper) {
@@ -784,41 +789,7 @@ public final class GenericUtils {
      * @return The wrapping instance
      */
     public static <T> Iterable<T> multiIterableSuppliers(Iterable<? extends 
Supplier<? extends Iterable<? extends T>>> providers) {
-        return (providers == null) ? Collections.emptyList() : () -> new 
Iterator<T>() {
-            private final Iterator<? extends Supplier<? extends Iterable<? 
extends T>>> iter = iteratorOf(providers);
-            private Iterator<? extends T> current = nextIterator();
-
-            @Override
-            public boolean hasNext() {
-                return current != null;
-            }
-
-            @Override
-            public T next() {
-                if (current == null) {
-                    throw new NoSuchElementException("No more elements");
-                }
-
-                T value = current.next();
-                if (!current.hasNext()) {
-                    current = nextIterator();
-                }
-
-                return value;
-            }
-
-            private Iterator<? extends T> nextIterator() {
-                while (iter.hasNext()) {
-                    Supplier<? extends Iterable<? extends T>> supplier = 
iter.next();
-                    Iterator<? extends T> values = iteratorOf((supplier == 
null) ? null : supplier.get());
-                    if (values.hasNext()) {
-                        return values;
-                    }
-                }
-
-                return null;
-            }
-        };
+        return () -> stream(providers).flatMap(s -> stream(s.get())).map(u -> 
(T) u).iterator();
     }
 
     public static <K, V> MapBuilder<K, V> mapBuilder() {

Reply via email to