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 2018/11/07 18:01:08 UTC

[5/5] mina-sshd git commit: [SSHD-860] Using lazy iterator in AuthenticationIdentitiesProvider#wrap

[SSHD-860] Using lazy iterator in AuthenticationIdentitiesProvider#wrap


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

Branch: refs/heads/master
Commit: 044a1ca8dbdbfdd386108a571d5c130b53e4432b
Parents: 99dd4aa
Author: Lyor Goldstein <lg...@apache.org>
Authored: Wed Nov 7 07:50:11 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Wed Nov 7 20:06:39 2018 +0200

----------------------------------------------------------------------
 .../auth/AuthenticationIdentitiesProvider.java  |  31 +----
 .../apache/sshd/common/util/GenericUtils.java   | 116 ++++++++++++++++++-
 .../sshd/common/util/GenericUtilsTest.java      |  57 +++++++++
 3 files changed, 175 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/044a1ca8/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
index cfd1f85..3fa61ba 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
@@ -20,11 +20,7 @@
 package org.apache.sshd.client.auth;
 
 import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.Comparator;
-import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 
 import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
@@ -87,38 +83,17 @@ public interface AuthenticationIdentitiesProvider extends KeyIdentityProvider, P
         return new AuthenticationIdentitiesProvider() {
             @Override
             public Iterable<KeyPair> loadKeys() {
-                return selectIdentities(KeyPair.class);
+                return GenericUtils.lazySelectMatchingTypes(identities, KeyPair.class);
             }
 
             @Override
             public Iterable<String> loadPasswords() {
-                return selectIdentities(String.class);
+                return GenericUtils.lazySelectMatchingTypes(identities, String.class);
             }
 
             @Override
             public Iterable<?> loadIdentities() {
-                return selectIdentities(Object.class);
-            }
-
-            // NOTE: returns a NEW Collection on every call so that the original
-            //      identities remain unchanged
-            private <T> Collection<T> selectIdentities(Class<T> type) {
-                Collection<T> matches = null;
-                for (Iterator<?> iter = GenericUtils.iteratorOf(identities); iter.hasNext();) {
-                    Object o = iter.next();
-                    Class<?> t = o.getClass();
-                    if (!type.isAssignableFrom(t)) {
-                        continue;
-                    }
-
-                    if (matches == null) {
-                        matches = new LinkedList<>();
-                    }
-
-                    matches.add(type.cast(o));
-                }
-
-                return (matches == null) ? Collections.<T>emptyList() : matches;
+                return GenericUtils.lazySelectMatchingTypes(identities, Object.class);
             }
         };
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/044a1ca8/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index c924d1e..52cf171 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -36,6 +36,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.NavigableMap;
 import java.util.NavigableSet;
+import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
@@ -867,6 +868,118 @@ public final class GenericUtils {
     }
 
     /**
+     * @param <T> Generic selected identities type
+     * @param The source values - ignored if {@code null}
+     * @param type The (never @code null) type of values to select - any value
+     * whose type is assignable to this type will be selected by the iterator.
+     * @return {@link Iterable} whose {@link Iterator} selects only values
+     * matching the specific type. <b>Note:</b> the matching values are not
+     * pre-calculated (hence the &quot;lazy&quot; denomination) - i.e.,
+     * the match is performed only when {@link Iterator#hasNext()} is called.
+     */
+    public static <T> Iterable<T> lazySelectMatchingTypes(Iterable<?> values, Class<T> type) {
+        Objects.requireNonNull(type, "No type selector specified");
+        if (values == null) {
+            return Collections.emptyList();
+        }
+
+        return new Iterable<T>() {
+            @Override
+            public Iterator<T> iterator() {
+                return lazySelectMatchingTypes(values.iterator(), type);
+            }
+
+            @Override
+            public String toString() {
+                return Iterable.class.getSimpleName() + "[lazy-select](" + type.getSimpleName() + ")";
+            }
+        };
+    }
+
+    /**
+     * @param <T> Generic selected identities type
+     * @param The source values - ignored if {@code null}
+     * @param type The (never @code null) type of values to select - any value
+     * whose type is assignable to this type will be selected by the iterator.
+     * @return An {@link Iterator} whose {@code next()} call selects only values
+     * matching the specific type. <b>Note:</b> the matching values are not
+     * pre-calculated (hence the &quot;lazy&quot; denomination) - i.e.,
+     * the match is performed only when {@link Iterator#hasNext()} is called.
+     */
+    public static <T> Iterator<T> lazySelectMatchingTypes(Iterator<?> values, Class<T> type) {
+        Objects.requireNonNull(type, "No type selector specified");
+        if (values == null) {
+            return Collections.emptyIterator();
+        }
+
+        return new Iterator<T>() {
+            private boolean finished;
+            private T nextValue;
+
+            @Override
+            public boolean hasNext() {
+                if (finished) {
+                    return false;
+                }
+
+                nextValue = selectNextMatchingValue(values, type);
+                if (nextValue == null) {
+                    finished = true;
+                }
+
+                return !finished;
+            }
+
+            @Override
+            public T next() {
+                if (finished) {
+                    throw new NoSuchElementException("All values have been exhausted");
+                }
+                if (nextValue == null) {
+                    throw new IllegalStateException("'next()' called without asking 'hasNext()'");
+                }
+
+                T v = nextValue;
+                nextValue = null;   // so it will be re-fetched when 'hasNext' is called
+                return v;
+            }
+
+            @Override
+            public String toString() {
+                return Iterator.class.getSimpleName() + "[lazy-select](" + type.getSimpleName() + ")";
+            }
+        };
+    }
+
+    /**
+     * @param <T> Generic return type
+     * @param The source values - ignored if {@code null}
+     * @param type The (never @code null) type of values to select - any value
+     * whose type is assignable to this type will be selected by the iterator.
+     * @return The first value that matches the specified type - {@code null} if none found
+     */
+    public static <T> T selectNextMatchingValue(Iterator<?> values, Class<T> type) {
+        Objects.requireNonNull(type, "No type selector specified");
+        if (values == null) {
+            return null;
+        }
+
+        while (values.hasNext()) {
+            Object o = values.next();
+            if (o == null) {
+                continue;
+            }
+
+            Class<?> t = o.getClass();
+            if (type.isAssignableFrom(t)) {
+                return type.cast(o);
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * Wraps a group of {@link Supplier}s of {@link Iterable} instances into a &quot;unified&quot;
      * {@link Iterable} of their values, in the same order as the suppliers - i.e., once the values
      * from a specific supplier are exhausted, the next one is consulted, and so on, until all
@@ -876,7 +989,8 @@ public final class GenericUtils {
      * @param providers The providers - ignored if {@code null} (i.e., return an empty iterable instance)
      * @return The wrapping instance
      */
-    public static <T> Iterable<T> multiIterableSuppliers(Iterable<? extends Supplier<? extends Iterable<? extends T>>> providers) {
+    public static <T> Iterable<T> multiIterableSuppliers(
+            Iterable<? extends Supplier<? extends Iterable<? extends T>>> providers) {
         return () -> stream(providers).<T>flatMap(s -> stream(s.get())).map(Function.identity()).iterator();
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/044a1ca8/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
index 2b25f4a..8470385 100644
--- a/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
@@ -19,10 +19,21 @@
 
 package org.apache.sshd.common.util;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.temporal.Temporal;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.apache.sshd.util.test.JUnitTestSupport;
 import org.apache.sshd.util.test.NoIoTestCase;
@@ -173,4 +184,50 @@ public class GenericUtilsTest extends JUnitTestSupport {
         assertEquals("s1 vs. s2", Integer.signum(s1.compareTo(s2)), Integer.signum(GenericUtils.compare(c1, c2)));
         assertEquals("s2 vs. s1", Integer.signum(s2.compareTo(s1)), Integer.signum(GenericUtils.compare(c2, c1)));
     }
+
+    @Test
+    public void testLazySelectMatchingTypes() {
+        Collection<String> strings = Arrays.asList(
+            getCurrentTestName(),
+            getClass().getSimpleName(),
+            getClass().getPackage().getName());
+        Collection<Temporal> times = Arrays.asList(
+            LocalDateTime.now(),
+            LocalTime.now(),
+            LocalDate.now());
+        List<Object> values = Stream.concat(strings.stream(), times.stream()).collect(Collectors.toList());
+        AtomicInteger matchCount = new AtomicInteger(0);
+        for (int index = 1, count = values.size(); index <= count; index++) {
+            Collections.shuffle(values);
+            Class<?> type = ((index & 0x01) == 0) ? String.class : Temporal.class;
+            Iterator<?> lazy = GenericUtils.lazySelectMatchingTypes(
+                new Iterator<Object>() {
+                    private final Iterator<?> iter = values.iterator();
+
+                    {
+                        matchCount.set(0);
+                    }
+
+                    @Override
+                    public boolean hasNext() {
+                        return iter.hasNext();
+                    }
+
+                    @Override
+                    public Object next() {
+                        Object v = iter.next();
+                        if (type.isInstance(v)) {
+                            matchCount.incrementAndGet();
+                        }
+                        return v;
+                    }
+                }, type);
+            Set<?> expected = (type == String.class) ? new HashSet<>(strings) : new HashSet<>(times);
+            for (int c = 1; lazy.hasNext(); c++) {
+                Object o = lazy.next();
+                assertEquals("Mismatched match count for " + o, c, matchCount.get());
+                assertTrue("Unexpected value: " + o, expected.remove(o));
+            }
+        }
+    }
 }