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/09/06 16:03:50 UTC

[40/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
new file mode 100644
index 0000000..1620bad
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EventListener;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class EventListenerUtils {
+    /**
+     * A special &quot;comparator&quot; whose only purpose is to ensure
+     * there are no same references in a listener's set - to be used
+     * in conjunction with a {@code TreeSet} as its comparator
+     */
+    @SuppressWarnings("checkstyle:anoninnerlength")
+    public static final Comparator<EventListener> LISTENER_INSTANCE_COMPARATOR = (l1, l2) -> {
+        if (l1 == l2) {
+            return 0;
+        } else if (l1 == null) {
+            return 1;
+        } else if (l2 == null) {
+            return -1;
+        }
+
+        Class<?> c1 = l1.getClass();
+        Class<?> c2 = l2.getClass();
+        boolean checkHashCodes = true;
+        if (Proxy.isProxyClass(c1)) {
+            if (Proxy.isProxyClass(c2)) {
+                checkHashCodes = false; // cannot call hashCode on a proxy
+            } else {
+                return 1;
+            }
+        } else if (Proxy.isProxyClass(c2)) {
+            return -1;
+        }
+
+        if (checkHashCodes) {
+            int nRes = Integer.compare(l1.hashCode(), l2.hashCode());
+            if (nRes != 0) {
+                return nRes;
+            }
+        }
+
+        int nRes = Integer.compare(System.identityHashCode(l1), System.identityHashCode(l2));
+        if (nRes != 0) {
+            return nRes;
+        }
+
+        if (c1 != c2) {
+            return c1.getName().compareTo(c2.getName());
+        }
+
+        String s1 = Objects.toString(l1.toString(), "");
+        String s2 = Objects.toString(l2.toString(), "");
+        nRes = s1.compareTo(s2);
+        if (nRes != 0) {
+            return nRes;
+        }
+        throw new UnsupportedOperationException("Ran out of options to compare instance of " + s1 + " vs. " + s2);
+    };
+
+    private EventListenerUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * @param <L> Type of {@link SshdEventListener} contained in the set
+     * @param listeners The listeners to pre-add to the create set - ignored
+     * if (@code null}/empty
+     * @return A (synchronized) {@link Set} for containing the listeners ensuring
+     * that if same listener instance is added repeatedly only <U>one</U>
+     * instance is actually contained
+     */
+    public static <L extends SshdEventListener> Set<L> synchronizedListenersSet(Collection<? extends L> listeners) {
+        Set<L> s = EventListenerUtils.synchronizedListenersSet();
+        if (GenericUtils.size(listeners) > 0) {
+            s.addAll(listeners);
+        }
+
+        return s;
+    }
+
+    /**
+     * @param <L> Type of {@link SshdEventListener} contained in the set
+     * @return A (synchronized) {@link Set} for containing the listeners ensuring
+     * that if same listener instance is added repeatedly only <U>one</U>
+     * instance is actually contained
+     * @see #LISTENER_INSTANCE_COMPARATOR
+     */
+    public static <L extends SshdEventListener> Set<L> synchronizedListenersSet() {
+        return Collections.synchronizedSet(new TreeSet<L>(LISTENER_INSTANCE_COMPARATOR));
+    }
+
+    /**
+     * Provides proxy wrapper around an {@link Iterable} container of listener
+     * interface implementation. <b>Note:</b> a listener interface is one whose
+     * invoked methods return <u>only</u> {@code void}.
+     *
+     * @param <T>          Generic listener type
+     * @param listenerType The expected listener <u>interface</u>
+     * @param listeners    An {@link Iterable} container of listeners to be invoked.
+     *                     <p>
+     *                     <b>Note(s):</b>
+     *                     </p>
+     *                     <ul>
+     *                     <li><p>
+     *                     The invocation order is same as the {@link Iterable} container
+     *                     </p></li>
+     *
+     *                     <li><p>
+     *                     If any of the invoked listener methods throws an exception, the
+     *                     rest of the listener are <u>not</u> invoked and the exception is
+     *                     propagated to the caller
+     *                     </p></li>
+     *
+     *                     <li><p>
+     *                     It is up to the <u>caller</u> to ensure that the container does
+     *                     not change while the proxy is invoked
+     *                     </p></li>
+     *                     </ul>
+     * @return A proxy wrapper implementing the same interface, but delegating
+     * the calls to the container
+     * @see #proxyWrapper(Class, ClassLoader, Iterable)
+     */
+    public static <T extends SshdEventListener> T proxyWrapper(Class<T> listenerType, Iterable<? extends T> listeners) {
+        return proxyWrapper(listenerType, listenerType.getClassLoader(), listeners);
+    }
+
+    /**
+     * Provides proxy wrapper around an {@link Iterable} container of listener
+     * interface implementation. <b>Note:</b> a listener interface is one whose
+     * invoked methods return <u>only</u> {@code void}.
+     *
+     * @param <T>          Generic {@link SshdEventListener} type
+     * @param listenerType The expected listener <u>interface</u>
+     * @param loader       The {@link ClassLoader} to use for the proxy
+     * @param listeners    An {@link Iterable} container of listeners to be invoked.
+     *                     <p>
+     *                     <b>Note(s):</b>
+     *                     </p>
+     *                     <ul>
+     *                     <li><p>
+     *                     The invocation order is same as the {@link Iterable} container
+     *                     </p></li>
+     *
+     *                     <li><p>
+     *                     If any of the invoked listener methods throws an exception, the
+     *                     rest of the listener are <u>not</u> invoked and the exception is
+     *                     propagated to the caller
+     *                     </p></li>
+     *
+     *                     <li><p>
+     *                     It is up to the <u>caller</u> to ensure that the container does
+     *                     not change while the proxy is invoked
+     *                     </p></li>
+     *                     </ul>
+     * @return A proxy wrapper implementing the same interface, but delegating
+     * the calls to the container
+     * @throws IllegalArgumentException if <tt>listenerType</tt> is not an interface
+     *                                  or a {@code null} container has been provided
+     * @see #proxyWrapper(Class, ClassLoader, Iterable)
+     */
+    public static <T extends SshdEventListener> T proxyWrapper(Class<T> listenerType, ClassLoader loader, final Iterable<? extends T> listeners) {
+        Objects.requireNonNull(listenerType, "No listener type specified");
+        ValidateUtils.checkTrue(listenerType.isInterface(), "Target proxy is not an interface: %s", listenerType.getSimpleName());
+        Objects.requireNonNull(listeners, "No listeners container provided");
+
+        Object wrapper = Proxy.newProxyInstance(loader, new Class<?>[]{listenerType}, (proxy, method, args) -> {
+            Throwable err = null;
+            for (T l : listeners) {
+                try {
+                    method.invoke(l, args);
+                } catch (Throwable t) {
+                    Throwable e = GenericUtils.peelException(t);
+                    err = GenericUtils.accumulateException(err, e);
+                }
+            }
+
+            if (err != null) {
+                throw err;
+            }
+
+            return null;    // we assume always void return value...
+        });
+        return listenerType.cast(wrapper);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java b/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java
new file mode 100644
index 0000000..c041f1b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+/**
+ * Notify about the occurrence of an event
+ *
+ * @param <E> type of event being notified
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface EventNotifier<E> {
+    /**
+     * @param event The event
+     * @throws Exception If failed to process the event notification
+     */
+    void notifyEvent(E event) throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/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
new file mode 100644
index 0000000..c924d1e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -0,0 +1,915 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BinaryOperator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class GenericUtils {
+
+    public static final byte[] EMPTY_BYTE_ARRAY = {};
+    public static final char[] EMPTY_CHAR_ARRAY = {};
+    public static final String[] EMPTY_STRING_ARRAY = {};
+    public static final Object[] EMPTY_OBJECT_ARRAY = {};
+
+    /**
+     * A value indicating a {@code null} value - to be used as a placeholder
+     * where {@code null}s are not allowed
+     */
+    public static final Object NULL = new Object();
+
+    /**
+     * The complement of {@link String#CASE_INSENSITIVE_ORDER}
+     */
+    public static final Comparator<String> CASE_SENSITIVE_ORDER = (s1, s2) -> {
+        if (s1 == s2) {
+            return 0;
+        } else {
+            return s1.compareTo(s2);
+        }
+    };
+
+    public static final String QUOTES = "\"'";
+
+    @SuppressWarnings("rawtypes")
+    private static final Supplier CASE_INSENSITIVE_MAP_FACTORY = () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    private GenericUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static String trimToEmpty(String s) {
+        if (s == null) {
+            return "";
+        } else {
+            return s.trim();
+        }
+    }
+
+    public static String replaceWhitespaceAndTrim(String s) {
+        if (s != null) {
+            s = s.replace('\t', ' ');
+        }
+
+        return trimToEmpty(s);
+    }
+
+    /**
+     * @param s The {@link String} value to calculate the hash code on - may
+     * be {@code null}/empty in which case a value of zero is returned
+     * @return The calculated hash code
+     * @see #hashCode(String, Boolean)
+     */
+    public static int hashCode(String s) {
+        return hashCode(s, null);
+    }
+
+    /**
+     * @param s The {@link String} value to calculate the hash code on - may
+     * be {@code null}/empty in which case a value of zero is returned
+     * @param useUppercase Whether to convert the string to uppercase, lowercase
+     * or not at all:
+     * <UL>
+     *      <LI>{@code null} - no conversion</LI>
+     *      <LI>{@link Boolean#TRUE} - get hash code of uppercase</LI>
+     *      <LI>{@link Boolean#FALSE} - get hash code of lowercase</LI>
+     * </UL>
+     * @return The calculated hash code
+     */
+    public static int hashCode(String s, Boolean useUppercase) {
+        if (isEmpty(s)) {
+            return 0;
+        } else if (useUppercase == null) {
+            return s.hashCode();
+        } else if (useUppercase.booleanValue()) {
+            return s.toUpperCase().hashCode();
+        } else {
+            return s.toLowerCase().hashCode();
+        }
+    }
+
+    public static int safeCompare(String s1, String s2, boolean caseSensitive) {
+        if (isSameReference(s1, s2)) {
+            return 0;
+        } else if (s1 == null) {
+            return +1;    // push null(s) to end
+        } else if (s2 == null) {
+            return -1;    // push null(s) to end
+        } else if (caseSensitive) {
+            return s1.compareTo(s2);
+        } else {
+            return s1.compareToIgnoreCase(s2);
+        }
+    }
+
+    public static <T> boolean isSameReference(T o1, T o2) {
+        return o1 == o2;
+    }
+
+    public static int length(CharSequence cs) {
+        return cs == null ? 0 : cs.length();
+    }
+
+    public static boolean isEmpty(CharSequence cs) {
+        return length(cs) <= 0;
+    }
+
+    public static boolean isNotEmpty(CharSequence cs) {
+        return !isEmpty(cs);
+    }
+
+    public static int indexOf(CharSequence cs, char c) {
+        int len = length(cs);
+        for (int pos = 0; pos < len; pos++) {
+            char ch = cs.charAt(pos);
+            if (ch == c) {
+                return pos;
+            }
+        }
+
+        return -1;
+    }
+
+    public static int lastIndexOf(CharSequence cs, char c) {
+        int len = length(cs);
+        for (int pos = len - 1; pos >= 0; pos--) {
+            char ch = cs.charAt(pos);
+            if (ch == c) {
+                return pos;
+            }
+        }
+
+        return -1;
+    }
+
+    // a List would be better, but we want to be compatible with String.split(...)
+    public static String[] split(String s, char ch) {
+        if (isEmpty(s)) {
+            return EMPTY_STRING_ARRAY;
+        }
+
+        int lastPos = 0;
+        int curPos = s.indexOf(ch);
+        if (curPos < 0) {
+            return new String[]{s};
+        }
+
+        Collection<String> values = new LinkedList<>();
+        do {
+            String v = s.substring(lastPos, curPos);
+            values.add(v);
+
+            // skip separator
+            lastPos = curPos + 1;
+            if (lastPos >= s.length()) {
+                break;
+            }
+
+            curPos = s.indexOf(ch, lastPos);
+            if (curPos < lastPos) {
+                break;  // no more separators
+            }
+        } while (curPos < s.length());
+
+        // check if any leftovers
+        if (lastPos < s.length()) {
+            String v = s.substring(lastPos);
+            values.add(v);
+        }
+
+        return values.toArray(new String[values.size()]);
+    }
+
+    public static <T> String join(T[] values, char ch) {
+        return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), ch);
+    }
+
+    public static String join(Iterable<?> iter, char ch) {
+        return join((iter == null) ? null : iter.iterator(), ch);
+    }
+
+    public static String join(Iterator<?> iter, char ch) {
+        if ((iter == null) || (!iter.hasNext())) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        do {    // we already asked hasNext...
+            Object o = iter.next();
+            if (sb.length() > 0) {
+                sb.append(ch);
+            }
+            sb.append(Objects.toString(o));
+        } while (iter.hasNext());
+
+        return sb.toString();
+    }
+
+    public static <T> String join(T[] values, CharSequence sep) {
+        return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), sep);
+    }
+
+    public static String join(Iterable<?> iter, CharSequence sep) {
+        return join((iter == null) ? null : iter.iterator(), sep);
+    }
+
+    public static String join(Iterator<?> iter, CharSequence sep) {
+        if ((iter == null) || (!iter.hasNext())) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        do {    // we already asked hasNext...
+            Object o = iter.next();
+            if (sb.length() > 0) {
+                sb.append(sep);
+            }
+            sb.append(Objects.toString(o));
+        } while (iter.hasNext());
+
+        return sb.toString();
+    }
+
+    public static int size(Collection<?> c) {
+        return c == null ? 0 : c.size();
+    }
+
+    public static boolean isEmpty(Collection<?> c) {
+        return (c == null) || c.isEmpty();
+    }
+
+    public static boolean isNotEmpty(Collection<?> c) {
+        return !isEmpty(c);
+    }
+
+    public static int size(Map<?, ?> m) {
+        return m == null ? 0 : m.size();
+    }
+
+    public static boolean isEmpty(Map<?, ?> m) {
+        return (m == null) || m.isEmpty();
+    }
+
+    public static boolean isNotEmpty(Map<?, ?> m) {
+        return !isEmpty(m);
+    }
+
+    @SafeVarargs
+    public static <T> int length(T... a) {
+        return a == null ? 0 : a.length;
+    }
+
+    public static <T> boolean isEmpty(Iterable<? extends T> iter) {
+        if (iter == null) {
+            return true;
+        } else if (iter instanceof Collection<?>) {
+            return isEmpty((Collection<?>) iter);
+        } else {
+            return isEmpty(iter.iterator());
+        }
+    }
+
+    public static <T> boolean isNotEmpty(Iterable<? extends T> iter) {
+        return !isEmpty(iter);
+    }
+
+    public static <T> boolean isEmpty(Iterator<? extends T> iter) {
+        return iter == null || !iter.hasNext();
+    }
+
+    public static <T> boolean isNotEmpty(Iterator<? extends T> iter) {
+        return !isEmpty(iter);
+    }
+
+    @SafeVarargs
+    public static <T> boolean isEmpty(T... a) {
+        return length(a) <= 0;
+    }
+
+    public static int length(char[] chars) {
+        return (chars == null) ? 0 : chars.length;
+    }
+
+    public static boolean isEmpty(char[] chars) {
+        return length(chars) <= 0;
+    }
+
+    /**
+     * Compares 2 character arrays - <B>Note:</B> {@code null} and empty
+     * are considered <U>equal</U>
+     *
+     * @param c1 1st array
+     * @param c2 2nd array
+     * @return Negative is 1st array comes first in lexicographical order,
+     * positive if 2nd array comes first and zero if equal
+     */
+    public static int compare(char[] c1, char[] c2) {
+        int l1 = length(c1);
+        int l2 = length(c2);
+        int cmpLen = Math.min(l1, l2);
+        for (int index = 0; index < cmpLen; index++) {
+            char c11 = c1[index];
+            char c22 = c2[index];
+            int nRes = Character.compare(c11, c22);
+            if (nRes != 0) {
+                return nRes;
+            }
+        }
+
+        int nRes = Integer.compare(l1, l2);
+        if (nRes != 0) {
+            return nRes;
+        }
+
+        return 0;
+    }
+
+    @SafeVarargs    // there is no EnumSet.of(...) so we have to provide our own
+    public static <E extends Enum<E>> Set<E> of(E... values) {
+        return of(isEmpty(values) ? Collections.emptySet() : Arrays.asList(values));
+    }
+
+    public static <E extends Enum<E>> Set<E> of(Collection<? extends E> values) {
+        if (isEmpty(values)) {
+            return Collections.emptySet();
+        }
+
+        Set<E> result = null;
+        for (E v : values) {
+            /*
+             * A trick to compensate for the fact that we do not have
+             * the enum Class to invoke EnumSet.noneOf
+             */
+            if (result == null) {
+                result = EnumSet.of(v);
+            } else {
+                result.add(v);
+            }
+        }
+
+        return result;
+    }
+
+    public static <T> boolean containsAny(Collection<? extends T> coll, Iterable<? extends T> values) {
+        if (isEmpty(coll)) {
+            return false;
+        }
+
+        for (T v : values) {
+            if (coll.contains(v)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static <T> void forEach(Iterable<T> values, Consumer<T> consumer) {
+        if (isNotEmpty(values)) {
+            values.forEach(consumer);
+        }
+    }
+
+    public static <T, U> List<U> map(Collection<T> values, Function<? super T, ? extends U> mapper) {
+        return stream(values).map(mapper).collect(Collectors.toList());
+    }
+
+    public static <T, U> NavigableSet<U> mapSort(
+            Collection<T> values, Function<? super T, ? extends U> mapper, Comparator<U> comparator) {
+        return stream(values).map(mapper).collect(toSortedSet(comparator));
+    }
+
+    public static <T, K, U> NavigableMap<K, U> toSortedMap(
+            Iterable<T> values, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Comparator<K> comparator) {
+        return stream(values).collect(toSortedMap(keyMapper, valueMapper, comparator));
+    }
+
+    public static <T, K, U> Collector<T, ?, NavigableMap<K, U>> toSortedMap(
+            Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Comparator<K> comparator) {
+        return Collectors.toMap(keyMapper, valueMapper, throwingMerger(), () -> new TreeMap<>(comparator));
+    }
+
+    private static <T> BinaryOperator<T> throwingMerger() {
+        return (u, v) -> {
+            throw new IllegalStateException(String.format("Duplicate key %s", u));
+        };
+    }
+
+    public static <T> Collector<T, ?, NavigableSet<T>> toSortedSet(Comparator<T> comparator) {
+        return Collectors.toCollection(() -> new TreeSet<>(comparator));
+    }
+
+    public static <T> Stream<T> stream(Iterable<T> values) {
+        if (isEmpty(values)) {
+            return Stream.empty();
+        } else if (values instanceof Collection<?>) {
+            return ((Collection<T>) values).stream();
+        } else {
+            return StreamSupport.stream(values.spliterator(), false);
+        }
+    }
+
+    @SafeVarargs
+    public static <T> List<T> unmodifiableList(T... values) {
+        return unmodifiableList(asList(values));
+    }
+
+    public static <T> List<T> unmodifiableList(Collection<? extends T> values) {
+        if (isEmpty(values)) {
+            return Collections.emptyList();
+        } else {
+            return Collections.unmodifiableList(new ArrayList<>(values));
+        }
+    }
+
+    public static <T> List<T> unmodifiableList(Stream<T> values) {
+        return unmodifiableList(values.collect(Collectors.toList()));
+    }
+
+    @SafeVarargs
+    public static <T> List<T> asList(T... values) {
+        return isEmpty(values) ? Collections.emptyList() : Arrays.asList(values);
+    }
+
+    @SafeVarargs
+    public static <T> Set<T> asSet(T... values) {
+        return new HashSet<>(asList(values));
+    }
+
+    @SafeVarargs
+    public static <V extends Comparable<V>> NavigableSet<V> asSortedSet(V... values) {
+        return asSortedSet(Comparator.naturalOrder(), values);
+    }
+
+    public static <V extends Comparable<V>> NavigableSet<V> asSortedSet(Collection<? extends V> values) {
+        return asSortedSet(Comparator.naturalOrder(), values);
+    }
+
+    /**
+     * @param <V>    The element type
+     * @param comp   The (non-{@code null}) {@link Comparator} to use
+     * @param values The values to be added (ignored if {@code null})
+     * @return A {@link NavigableSet} containing the values (if any) sorted
+     * using the provided comparator
+     */
+    @SafeVarargs
+    public static <V> NavigableSet<V> asSortedSet(Comparator<? super V> comp, V... values) {
+        return asSortedSet(comp, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+    }
+
+    /**
+     * @param <V>    The element type
+     * @param comp   The (non-{@code null}) {@link Comparator} to use
+     * @param values The values to be added (ignored if {@code null}/empty)
+     * @return A {@link NavigableSet} containing the values (if any) sorted
+     * using the provided comparator
+     */
+    public static <V> NavigableSet<V> asSortedSet(Comparator<? super V> comp, Collection<? extends V> values) {
+        NavigableSet<V> set = new TreeSet<>(Objects.requireNonNull(comp, "No comparator"));
+        if (size(values) > 0) {
+            set.addAll(values);
+        }
+        return set;
+    }
+
+    /**
+     * @param <V> Type of mapped value
+     * @return A {@link Supplier} that returns a <U>new</U> {@link NavigableMap}
+     * whenever its {@code get()} method is invoked
+     */
+    @SuppressWarnings("unchecked")
+    public static <V> Supplier<NavigableMap<String, V>> caseInsensitiveMap() {
+        return CASE_INSENSITIVE_MAP_FACTORY;
+    }
+
+    /**
+     * Flips between keys and values of an input map
+     *
+     * @param <K> Original map key type
+     * @param <V> Original map value type
+     * @param <M> Flipped map type
+     * @param map The original map to flip
+     * @param mapCreator The creator of the target map
+     * @param allowDuplicates Whether to ignore duplicates on flip
+     * @return The flipped map result
+     * @throws IllegalArgumentException if <tt>allowDuplicates</tt> is {@code false}
+     * and a duplicate value found in the original map.
+     */
+    public static <K, V, M extends Map<V, K>> M flipMap(
+            Map<? extends K, ? extends V> map, Supplier<? extends M> mapCreator, boolean allowDuplicates) {
+        M result = Objects.requireNonNull(mapCreator.get(), "No map created");
+        map.forEach((key, value) -> {
+            K prev = result.put(value, key);
+            if ((prev != null) && (!allowDuplicates)) {
+                ValidateUtils.throwIllegalArgumentException("Multiple values for key=%s: current=%s, previous=%s", value, key, prev);
+            }
+        });
+
+        return result;
+    }
+
+    @SafeVarargs
+    public static <K, V, M extends Map<K, V>> M mapValues(
+            Function<? super V, ? extends K> keyMapper, Supplier<? extends M> mapCreator, V... values) {
+        return mapValues(keyMapper, mapCreator, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+    }
+
+    /**
+     * Creates a map out of a group of values
+     *
+     * @param <K> The key type
+     * @param <V> The value type
+     * @param <M> The result {@link Map} type
+     * @param keyMapper The {@link Function} that generates a key for a given value.
+     * If the returned key is {@code null} then the value is not mapped
+     * @param mapCreator The {@link Supplier} used to create/retrieve the result map - provided
+     * non-empty group of values
+     * @param values The values to be mapped
+     * @return The resulting {@link Map} - <B>Note:</B> no validation is made to ensure
+     * that 2 (or more) values are not mapped to the same key
+     */
+    public static <K, V, M extends Map<K, V>> M mapValues(
+            Function<? super V, ? extends K> keyMapper, Supplier<? extends M> mapCreator, Collection<? extends V> values) {
+        M map = mapCreator.get();
+        for (V v : values) {
+            K k = keyMapper.apply(v);
+            if (k == null) {
+                continue;   // debug breakpoint
+            }
+            map.put(k, v);
+        }
+
+        return map;
+    }
+
+    @SafeVarargs
+    public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, T... values) {
+        return findFirstMatchingMember(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+    }
+
+    public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, Collection<? extends T> values) {
+        List<T> matches = selectMatchingMembers(acceptor, values);
+        return GenericUtils.isEmpty(matches) ? null : matches.get(0);
+    }
+
+    /**
+     * Returns a list of all the values that were accepted by a predicate
+     *
+     * @param <T> The type of value being evaluated
+     * @param acceptor The {@link Predicate} to consult whether a member is selected
+     * @param values The values to be scanned
+     * @return A {@link List} of all the values that were accepted by the predicate
+     */
+    @SafeVarargs
+    public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, T... values) {
+        return selectMatchingMembers(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+    }
+
+    /**
+     * Returns a list of all the values that were accepted by a predicate
+     *
+     * @param <T> The type of value being evaluated
+     * @param acceptor The {@link Predicate} to consult whether a member is selected
+     * @param values The values to be scanned
+     * @return A {@link List} of all the values that were accepted by the predicate
+     */
+    public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, Collection<? extends T> values) {
+        return GenericUtils.stream(values)
+                .filter(acceptor)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * @param s The {@link CharSequence} to be checked
+     * @return If the sequence contains any of the {@link #QUOTES}
+     * on <U>both</U> ends, then they are stripped, otherwise
+     * nothing is done
+     * @see #stripDelimiters(CharSequence, char)
+     */
+    public static CharSequence stripQuotes(CharSequence s) {
+        if (isEmpty(s)) {
+            return s;
+        }
+
+        for (int index = 0; index < QUOTES.length(); index++) {
+            char delim = QUOTES.charAt(index);
+            CharSequence v = stripDelimiters(s, delim);
+            if (v != s) {   // if stripped one don't continue
+                return v;
+            }
+        }
+
+        return s;
+    }
+
+    /**
+     * @param s     The {@link CharSequence} to be checked
+     * @param delim The expected delimiter
+     * @return If the sequence contains the delimiter on <U>both</U> ends,
+     * then it is are stripped, otherwise nothing is done
+     */
+    public static CharSequence stripDelimiters(CharSequence s, char delim) {
+        if (isEmpty(s) || (s.length() < 2)) {
+            return s;
+        }
+
+        int lastPos = s.length() - 1;
+        if ((s.charAt(0) != delim) || (s.charAt(lastPos) != delim)) {
+            return s;
+        } else {
+            return s.subSequence(1, lastPos);
+        }
+    }
+
+    public static RuntimeException toRuntimeException(Throwable t) {
+        return toRuntimeException(t, true);
+    }
+
+    /**
+     * Converts a thrown generic exception to a {@link RuntimeException}
+     *
+     * @param t The original thrown exception
+     * @param peelThrowable Whether to determine the root cause by &quot;peeling&quot;
+     * any enclosing exceptions
+     * @return The thrown cause if already a runtime exception, otherwise a
+     * runtime exception of the resolved exception as its cause
+     * @see #peelException(Throwable)
+     */
+    public static RuntimeException toRuntimeException(Throwable t, boolean peelThrowable) {
+        Throwable e = peelThrowable ? peelException(t) : t;
+        if (e instanceof RuntimeException) {
+            return (RuntimeException) e;
+        }
+
+        return new RuntimeException(e);
+    }
+
+    /**
+     * Attempts to get to the &quot;effective&quot; exception being thrown,
+     * by taking care of some known exceptions that wrap the original thrown
+     * one.
+     *
+     * @param t The original {@link Throwable} - ignored if {@code null}
+     * @return The effective exception - same as input if not a wrapper
+     */
+    public static Throwable peelException(Throwable t) {
+        // NOTE: check order is important - e.g., InvocationTargetException extends ReflectiveOperationException
+        if (t == null) {
+            return t;
+        } else if (t instanceof UndeclaredThrowableException) {
+            Throwable wrapped = ((UndeclaredThrowableException) t).getUndeclaredThrowable();
+            // according to the Javadoc it may be null, in which case 'getCause'
+            // might contain the information we need
+            if (wrapped != null) {
+                return peelException(wrapped);
+            }
+
+            wrapped = t.getCause();
+            if (wrapped != t) {     // make sure it is a real cause
+                return peelException(wrapped);
+            }
+        } else if (t instanceof InvocationTargetException) {
+            Throwable target = ((InvocationTargetException) t).getTargetException();
+            if (target != null) {
+                return peelException(target);
+            }
+        } else if (t instanceof ReflectionException) {
+            Throwable target = ((ReflectionException) t).getTargetException();
+            if (target != null) {
+                return peelException(target);
+            }
+        } else if (t instanceof ExecutionException) {
+            Throwable wrapped = resolveExceptionCause(t);
+            if (wrapped != null) {
+                return peelException(wrapped);
+            }
+        } else if (t instanceof MBeanException) {
+            Throwable target = ((MBeanException) t).getTargetException();
+            if (target != null) {
+                return peelException(target);
+            }
+        }
+
+        return t;   // no special handling required or available
+    }
+
+    /**
+     * @param t The original {@link Throwable} - ignored if {@code null}
+     * @return If {@link Throwable#getCause()} is non-{@code null} then
+     * the cause, otherwise the original exception - {@code null} if
+     * the original exception was {@code null}
+     */
+    public static Throwable resolveExceptionCause(Throwable t) {
+        if (t == null) {
+            return t;
+        }
+
+        Throwable c = t.getCause();
+        if (c == null) {
+            return t;
+        } else {
+            return c;
+        }
+    }
+
+    /**
+     * Used to &quot;accumulate&quot; exceptions of the <U>same type</U>. If the
+     * current exception is {@code null} then the new one becomes the current,
+     * otherwise the new one is added as a <U>suppressed</U> exception to the
+     * current one
+     *
+     * @param <T>     The exception type
+     * @param current The current exception
+     * @param extra   The extra/new exception
+     * @return The resolved exception
+     * @see Throwable#addSuppressed(Throwable)
+     */
+    public static <T extends Throwable> T accumulateException(T current, T extra) {
+        if (current == null) {
+            return extra;
+        }
+
+        if ((extra == null) || (extra == current)) {
+            return current;
+        }
+
+        current.addSuppressed(extra);
+        return current;
+    }
+
+    public static IOException toIOException(Throwable e) {
+        if (e instanceof IOException) {
+            return (IOException) e;
+        } else {
+            return new IOException(e);
+        }
+    }
+
+    /**
+     * Wraps a value into a {@link Supplier}
+     * @param <T> Type of value being supplied
+     * @param value The value to be supplied
+     * @return The supplier wrapper
+     */
+    public static <T> Supplier<T> supplierOf(T value) {
+        return () -> value;
+    }
+
+    /**
+     * Resolves to an always non-{@code null} iterator
+     *
+     * @param <T> Type of value being iterated
+     * @param iterable The {@link Iterable} instance
+     * @return A non-{@code null} iterator which may be empty if no iterable
+     * instance or no iterator returned from it
+     * @see #iteratorOf(Iterator)
+     */
+    public static <T> Iterator<T> iteratorOf(Iterable<T> iterable) {
+        return iteratorOf((iterable == null) ? null : iterable.iterator());
+    }
+
+    /**
+     * @param <B> Generic base class
+     * @param <D> Generic child class
+     * @return An identity {@link Function} that returns its input child class as a base class
+     */
+    public static <B, D extends B> Function<D, B> downcast() {
+        return t -> t;
+    }
+
+    /**
+     * Resolves to an always non-{@code null} iterator
+     *
+     * @param <T> Type of value being iterated
+     * @param iter The {@link Iterator} instance
+     * @return  A non-{@code null} iterator which may be empty if no iterator instance
+     * @see Collections#emptyIterator()
+     */
+    public static <T> Iterator<T> iteratorOf(Iterator<T> iter) {
+        return (iter == null) ? Collections.emptyIterator() : iter;
+    }
+
+    public static <U, V> Iterable<V> wrapIterable(Iterable<? extends U> iter, Function<? super U, ? extends V> mapper) {
+        return () -> wrapIterator(iter, mapper);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <U, V> Iterator<V> wrapIterator(Iterable<? extends U> iter, Function<? super U, ? extends V> mapper) {
+        return (Iterator) stream(iter).map(mapper).iterator();
+    }
+
+    public static <U, V> Iterator<V> wrapIterator(Iterator<? extends U> iter, Function<? super U, ? extends V> mapper) {
+        Iterator<? extends U> iterator = iteratorOf(iter);
+        return new Iterator<V>() {
+            @Override
+            public boolean hasNext() {
+                return iterator.hasNext();
+            }
+
+            @Override
+            public V next() {
+                U value = iterator.next();
+                return mapper.apply(value);
+            }
+        };
+    }
+
+    /**
+     * 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
+     * suppliers have been consulted
+     *
+     * @param <T> Type of value being iterated
+     * @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) {
+        return () -> stream(providers).<T>flatMap(s -> stream(s.get())).map(Function.identity()).iterator();
+    }
+
+    public static <K, V> MapBuilder<K, V> mapBuilder() {
+        return new MapBuilder<>();
+    }
+
+    public static <K, V> MapBuilder<K, V> mapBuilder(Comparator<K> comparator) {
+        return new MapBuilder<>(comparator);
+    }
+
+    public static class MapBuilder<K, V> {
+        private Map<K, V> map;
+
+        public MapBuilder() {
+            this.map = new LinkedHashMap<>();
+        }
+
+        public MapBuilder(Comparator<? super K> comparator) {
+            this.map = new TreeMap<>(comparator);
+        }
+
+        public MapBuilder<K, V> put(K k, V v) {
+            map.put(k, v);
+            return this;
+        }
+
+        public Map<K, V> build() {
+            return map;
+        }
+
+        public Map<K, V> immutable() {
+            return Collections.unmodifiableMap(map);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java b/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
new file mode 100644
index 0000000..8f18bfe
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A dummy map that ignores all {@code put/remove} calls
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class IgnoringEmptyMap<K, V> implements Map<K, V> {
+    @SuppressWarnings("rawtypes")
+    private static final IgnoringEmptyMap INSTANCE = new IgnoringEmptyMap();
+
+    public IgnoringEmptyMap() {
+        super();
+    }
+
+    @Override
+    public int size() {
+        return 0;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return true;
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        Objects.requireNonNull(value, "No value provided");
+        return false;
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        Objects.requireNonNull(key, "No key provided");
+        return false;
+    }
+
+    @Override
+    public V get(Object key) {
+        Objects.requireNonNull(key, "No key provided");
+        return null;
+    }
+
+    @Override
+    public V put(K key, V value) {
+        Objects.requireNonNull(key, "No key provided");
+        Objects.requireNonNull(value, "No value provided");
+        return null;
+    }
+
+    @Override
+    public V remove(Object key) {
+        Objects.requireNonNull(key, "No key provided");
+        return null;
+    }
+
+    @Override
+    public void putAll(Map<? extends K, ? extends V> m) {
+        // ignored
+    }
+
+    @Override
+    public void clear() {
+        // ignored
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Collection<V> values() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof IgnoringEmptyMap<?, ?>;
+    }
+
+    @Override
+    public int hashCode() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "{}";
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+        return Collections.emptySet();
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <K, V> IgnoringEmptyMap<K, V> getInstance() {
+        return INSTANCE;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java b/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
new file mode 100644
index 0000000..490abb0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.function.IntUnaryOperator;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class Int2IntFunction {
+    private Int2IntFunction() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static IntUnaryOperator sub(int delta) {
+        return add(0 - delta);
+    }
+
+    public static IntUnaryOperator add(int delta) {
+        if (delta == 0) {
+            return IntUnaryOperator.identity();
+        } else {
+            return value -> value + delta;
+        }
+    }
+
+    public static IntUnaryOperator mul(int factor) {
+        if (factor == 0) {
+            return constant(0);
+        } else if (factor == 1) {
+            return IntUnaryOperator.identity();
+        } else {
+            return value -> value * factor;
+        }
+    }
+
+    public static IntUnaryOperator constant(int v) {
+        return value -> v;
+    }
+
+    public static IntUnaryOperator div(int factor) {
+        if (factor == 1) {
+            return IntUnaryOperator.identity();
+        } else {
+            ValidateUtils.checkTrue(factor != 0, "Zero division factor");
+            return value -> value / factor;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java b/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java
new file mode 100644
index 0000000..71cbebd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * The complement to the {@code Callable} interface - accepts one argument
+ * and possibly throws somethind
+ *
+ * @param <ARG> Argument type
+ * @param <RET> Return type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface Invoker<ARG, RET> {
+    RET invoke(ARG arg) throws Throwable;
+
+    static <ARG> Invoker<ARG, Void> wrapAll(Collection<? extends Invoker<? super ARG, ?>> invokers) {
+        return arg -> {
+            invokeAll(arg, invokers);
+            return null;
+        };
+    }
+
+    /**
+     * Invokes <U>all</U> the instances ignoring the return value. Any
+     * intermediate exceptions are accumulated and thrown at the end.
+     *
+     * @param <ARG> Argument type
+     * @param arg The argument to pass to the {@link #invoke(Object)} method
+     * @param invokers The invokers to scan - ignored if {@code null}/empty
+     * (also ignores {@code null} members)
+     * @throws Throwable If invocation failed
+     */
+    static <ARG> void invokeAll(ARG arg, Collection<? extends Invoker<? super ARG, ?>> invokers) throws Throwable {
+        if (GenericUtils.isEmpty(invokers)) {
+            return;
+        }
+
+        Throwable err = null;
+        for (Invoker<? super ARG, ?> i : invokers) {
+            if (i == null) {
+                continue;
+            }
+
+            try {
+                i.invoke(arg);
+            } catch (Throwable t) {
+                err = GenericUtils.accumulateException(err, t);
+            }
+        }
+
+        if (err != null) {
+            throw err;
+        }
+    }
+
+    static <ARG> Invoker<ARG, Void> wrapFirst(Collection<? extends Invoker<? super ARG, ?>> invokers) {
+        return arg -> {
+            Map.Entry<Invoker<? super ARG, ?>, Throwable> result = invokeTillFirstFailure(arg, invokers);
+            if (result != null) {
+                throw result.getValue();
+            }
+            return null;
+        };
+    }
+
+    /**
+     * Invokes all instances until 1st failure (if any)
+     *
+     * @param <ARG> Argument type
+     * @param arg The argument to pass to the {@link #invoke(Object)} method
+     * @param invokers The invokers to scan - ignored if {@code null}/empty
+     * (also ignores {@code null} members)
+     * @return A {@link SimpleImmutableEntry} representing the <U>first</U> failed
+     * invocation - {@code null} if all were successful (or none invoked).
+     */
+    static <ARG> SimpleImmutableEntry<Invoker<? super ARG, ?>, Throwable> invokeTillFirstFailure(ARG arg, Collection<? extends Invoker<? super ARG, ?>> invokers) {
+        if (GenericUtils.isEmpty(invokers)) {
+            return null;
+        }
+
+        for (Invoker<? super ARG, ?> i : invokers) {
+            if (i == null) {
+                continue;
+            }
+
+            try {
+                i.invoke(arg);
+            } catch (Throwable t) {
+                return new SimpleImmutableEntry<>(i, t);
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
new file mode 100644
index 0000000..6bcc93a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.util.Comparator;
+import java.util.Map;
+
+/**
+ * Represents an un-modifiable pair of values
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class MapEntryUtils {
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    private static final Comparator<Map.Entry<Comparable, ?>> BY_KEY_COMPARATOR = (o1, o2) -> {
+        Comparable k1 = o1.getKey();
+        Comparable k2 = o2.getKey();
+        return k1.compareTo(k2);
+    };
+
+    private MapEntryUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    /**
+     * @param <K> The {@link Comparable} key type
+     * @param <V> The associated entry value
+     * @return A {@link Comparator} for {@link java.util.Map.Entry}-ies that
+     * compares the key values
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static <K extends Comparable<K>, V> Comparator<Map.Entry<K, V>> byKeyEntryComparator() {
+        return (Comparator) BY_KEY_COMPARATOR;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java
new file mode 100644
index 0000000..c8e1817
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.IntStream;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class NumberUtils {
+    /**
+     * A {@link List} of all the {@link Class} types used to represent the
+     * primitive numerical values
+     */
+    public static final List<Class<?>> NUMERIC_PRIMITIVE_CLASSES =
+        GenericUtils.unmodifiableList(
+            Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE);
+
+    /**
+     * A {@link List} containing all the pure powers of 2 for a {@code long}
+     * value. The value at index <I>n</I> is 2 to the power of <I>n</I>
+     */
+    public static final List<Long> POWERS_OF_TWO =
+        GenericUtils.unmodifiableList(IntStream.range(0, 64).mapToObj(i -> 1L << i));
+
+    private NumberUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static boolean isPowerOf2(long value) {
+        for (Long l : POWERS_OF_TWO) {
+            if (value == l) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static long getNextPowerOf2(long value) {
+        long j = 1L;
+        while (j < value) {
+            j <<= 1;
+        }
+        return j;
+    }
+
+    public static int getNextPowerOf2(int value) {
+        int j = 1;
+        while (j < value) {
+            j <<= 1;
+        }
+        return j;
+    }
+
+    public static int hashCode(long... values) {
+        return Arrays.hashCode(values);
+    }
+
+    public static int hashCode(int... values) {
+        return Arrays.hashCode(values);
+    }
+
+    public static int hashCode(byte... values) {
+        return Arrays.hashCode(values);
+    }
+
+    public static int hashCode(byte[] a, int offset, int len) {
+        if (len == 0) {
+            return 0;
+        }
+
+        int result = 1;
+        for (int pos = offset, count = 0; count < len; pos++, count++) {
+            byte element = a[pos];
+            result = 31 * result + element;
+        }
+
+        return result;
+    }
+
+    public static int diffOffset(byte[] a1, int startPos1, byte[] a2, int startPos2, int len) {
+        for (int pos1 = startPos1, pos2 = startPos2, count = 0; count < len; pos1++, pos2++, count++) {
+            byte v1 = a1[pos1];
+            byte v2 = a2[pos2];
+            if (v1 != v2) {
+                return count;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * @param clazz The {@link Class} to examine - ignored if {@code null}
+     * @return If the class is a {@link Number} or one of the primitive numerical types
+     * @see #NUMERIC_PRIMITIVE_CLASSES
+     */
+    public static boolean isNumericClass(Class<?> clazz) {
+        if (clazz == null) {
+            return false;
+        }
+
+        // turns out that the primitive types are not assignable to Number
+        if (Number.class.isAssignableFrom(clazz)) {
+            return true;
+        }
+
+        return NUMERIC_PRIMITIVE_CLASSES.indexOf(clazz) >= 0;
+    }
+
+    /**
+     * Converts a {@link Number} into an {@link Integer} if not already such
+     *
+     * @param n The {@link Number} - ignored if {@code null}
+     * @return The equivalent {@link Integer} value
+     */
+    public static Integer toInteger(Number n) {
+        if (n == null) {
+            return null;
+        } else if (n instanceof Integer) {
+            return (Integer) n;
+        } else {
+            return n.intValue();
+        }
+    }
+
+    public static String join(CharSequence separator, long... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (long v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(v);
+        }
+
+        return sb.toString();
+    }
+
+    public static String join(char separator, long... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (long v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(v);
+        }
+
+        return sb.toString();
+    }
+
+    public static String join(CharSequence separator, boolean unsigned, byte... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (byte v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(unsigned ? (v & 0xFF) : v);
+        }
+
+        return sb.toString();
+    }
+
+    public static String join(char separator, boolean unsigned, byte... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (byte v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(unsigned ? (v & 0xFF) : v);
+        }
+
+        return sb.toString();
+    }
+
+    public static String join(CharSequence separator, int... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (int v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(v);
+        }
+
+        return sb.toString();
+    }
+
+    public static String join(char separator, int... values) {
+        if (NumberUtils.isEmpty(values)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+        for (int v : values) {
+            if (sb.length() > 0) {
+                sb.append(separator);
+            }
+            sb.append(v);
+        }
+
+        return sb.toString();
+    }
+
+    public static boolean isEmpty(byte[] a) {
+        return NumberUtils.length(a) <= 0;
+    }
+
+    public static boolean isEmpty(int[] a) {
+        return NumberUtils.length(a) <= 0;
+    }
+
+    public static boolean isEmpty(long[] a) {
+        return NumberUtils.length(a) <= 0;
+    }
+
+    public static int length(byte... a) {
+        return a == null ? 0 : a.length;
+    }
+
+    public static int length(int... a) {
+        return a == null ? 0 : a.length;
+    }
+
+    public static int length(long... a) {
+        return a == null ? 0 : a.length;
+    }
+
+    public static List<Integer> asList(int... values) {
+        int len = length(values);
+        if (len <= 0) {
+            return Collections.emptyList();
+        }
+
+        List<Integer> l = new ArrayList<>(len);
+        for (int v : values) {
+            l.add(v);
+        }
+
+        return l;
+    }
+
+    /**
+     * Checks if optional sign and all others are '0'-'9'
+     * @param cs The {@link CharSequence} to check
+     * @return {@code true} if valid integer number
+     */
+    public static boolean isIntegerNumber(CharSequence cs) {
+        if (GenericUtils.isEmpty(cs)) {
+            return false;
+        }
+
+        for (int index = 0; index < cs.length(); index++) {
+            char c = cs.charAt(0);
+            if ((c >= '0') && (c <= '9')) {
+                continue;
+            }
+
+            if ((c == '+') || (c == '-')) {
+                if (index == 0) {
+                    continue;
+                }
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
new file mode 100644
index 0000000..23884a2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.function.Supplier;
+
+/**
+ * A generic builder interface
+ *
+ * @param <T> Type of object being built
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface ObjectBuilder<T> extends Supplier<T> {
+    @Override
+    default T get() {
+        return build();
+    }
+
+    T build();
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
new file mode 100644
index 0000000..f0b2c7e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Operating system dependent utility methods.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class OsUtils {
+
+    /**
+     * Property that can be used to override the reported value from {@link #getCurrentUser()}.
+     * If not set then &quot;user.name&quot; system property is used
+     */
+    public static final String CURRENT_USER_OVERRIDE_PROP = "org.apache.sshd.currentUser";
+
+    /**
+     * Property that can be used to override the reported value from {@link #getJavaVersion()}.
+     * If not set then &quot;java.version&quot; system property is used
+     */
+    public static final String JAVA_VERSION_OVERRIDE_PROP = "org.apache.sshd.javaVersion";
+
+    /**
+     * Property that can be used to override the reported value from {@link #isWin32()}.
+     * If not set then &quot;os.name&quot; system property is used
+     */
+    public static final String OS_TYPE_OVERRIDE_PROP = "org.apache.sshd.osType";
+
+    public static final String WINDOWS_SHELL_COMMAND_NAME = "cmd.exe";
+    public static final String LINUX_SHELL_COMMAND_NAME = "/bin/sh";
+
+    public static final String ROOT_USER = "root";
+
+    public static final List<String> LINUX_COMMAND =
+            Collections.unmodifiableList(Arrays.asList(LINUX_SHELL_COMMAND_NAME, "-i", "-l"));
+    public static final List<String> WINDOWS_COMMAND =
+            Collections.unmodifiableList(Collections.singletonList(WINDOWS_SHELL_COMMAND_NAME));
+
+    private static final AtomicReference<String> CURRENT_USER_HOLDER = new AtomicReference<>(null);
+    private static final AtomicReference<VersionInfo> JAVA_VERSION_HOLDER = new AtomicReference<>(null);
+    private static final AtomicReference<Boolean> OS_TYPE_HOLDER = new AtomicReference<>(null);
+
+    private OsUtils() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    /**
+     * @return true if the host is a UNIX system (and not Windows).
+     */
+    public static boolean isUNIX() {
+        return !isWin32();
+    }
+
+    /**
+     * @return true if the host is Windows (and not UNIX).
+     * @see #OS_TYPE_OVERRIDE_PROP
+     * @see #setWin32(Boolean)
+     */
+    public static boolean isWin32() {
+        Boolean typeValue;
+        synchronized (OS_TYPE_HOLDER) {
+            typeValue = OS_TYPE_HOLDER.get();
+            if (typeValue != null) {    // is it the 1st time
+                return typeValue;
+            }
+
+            String value = System.getProperty(OS_TYPE_OVERRIDE_PROP, System.getProperty("os.name"));
+            typeValue = GenericUtils.trimToEmpty(value).toLowerCase().contains("windows");
+            OS_TYPE_HOLDER.set(typeValue);
+        }
+
+        return typeValue;
+    }
+
+    /**
+     * Can be used to enforce Win32 or Linux report from {@link #isWin32()} or {@link #isUNIX()}
+     * @param win32 The value to set - if {@code null} then O/S type is auto-detected
+     * @see #isWin32()
+     */
+    public static void setWin32(Boolean win32) {
+        synchronized (OS_TYPE_HOLDER) {
+            OS_TYPE_HOLDER.set(win32);
+        }
+    }
+
+    public static List<String> resolveDefaultInteractiveCommand() {
+        return resolveInteractiveCommand(isWin32());
+    }
+
+    public static List<String> resolveInteractiveCommand(boolean isWin32) {
+        if (isWin32) {
+            return WINDOWS_COMMAND;
+        } else {
+            return LINUX_COMMAND;
+        }
+    }
+
+    /**
+     * Get current user name
+     *
+     * @return Current user
+     * @see #CURRENT_USER_OVERRIDE_PROP
+     */
+    public static String getCurrentUser() {
+        String username = null;
+        synchronized (CURRENT_USER_HOLDER) {
+            username = CURRENT_USER_HOLDER.get();
+            if (username != null) {  // have we already resolved it ?
+                return username;
+            }
+
+            username = getCanonicalUser(System.getProperty(CURRENT_USER_OVERRIDE_PROP, System.getProperty("user.name")));
+            ValidateUtils.checkNotNullAndNotEmpty(username, "No username available");
+            CURRENT_USER_HOLDER.set(username);
+        }
+
+        return username;
+    }
+
+    /**
+     * Remove {@code Windows} domain and/or group prefix as well as &quot;(User);&quot suffix
+     *
+     * @param user The original username - ignored if {@code null}/empty
+     * @return The canonical user - unchanged if {@code Unix} O/S
+     */
+    public static String getCanonicalUser(String user) {
+        if (GenericUtils.isEmpty(user)) {
+            return user;
+        }
+
+        // Windows owner sometime has the domain and/or group prepended to it
+        if (isWin32()) {
+            int pos = user.lastIndexOf('\\');
+            if (pos > 0) {
+                user = user.substring(pos + 1);
+            }
+
+            pos = user.indexOf(' ');
+            if (pos > 0) {
+                user = user.substring(0, pos).trim();
+            }
+        }
+
+        return user;
+    }
+
+    /**
+     * Attempts to resolve canonical group name for {@code Windows}
+     *
+     * @param group The original group name - used if not {@code null}/empty
+     * @param user The owner name - sometimes it contains a group name
+     * @return The canonical group name
+     */
+    public static String resolveCanonicalGroup(String group, String user) {
+        if (isUNIX()) {
+            return group;
+        }
+
+        // we reach this code only for Windows
+        if (GenericUtils.isEmpty(group)) {
+            int pos = GenericUtils.isEmpty(user) ? -1 : user.lastIndexOf('\\');
+            return (pos > 0) ? user.substring(0, pos) : group;
+        }
+
+        int pos = group.indexOf(' ');
+        return (pos < 0) ? group : group.substring(0, pos).trim();
+    }
+
+    /**
+     * Can be used to programmatically set the username reported by {@link #getCurrentUser()}
+     * @param username The username to set - if {@code null} then {@link #CURRENT_USER_OVERRIDE_PROP}
+     * will be consulted
+     */
+    public static void setCurrentUser(String username) {
+        synchronized (CURRENT_USER_HOLDER) {
+            CURRENT_USER_HOLDER.set(username);
+        }
+    }
+
+    /**
+     * Resolves the reported Java version by consulting {@link #JAVA_VERSION_OVERRIDE_PROP}.
+     * If not set, then &quot;java.version&quot; property is used
+     * @return The resolved {@link VersionInfo} - never {@code null}
+     * @see #setJavaVersion(VersionInfo)
+     */
+    public static VersionInfo getJavaVersion() {
+        VersionInfo version;
+        synchronized (JAVA_VERSION_HOLDER) {
+            version = JAVA_VERSION_HOLDER.get();
+            if (version != null) {  // first time ?
+                return version;
+            }
+
+            String value = System.getProperty(JAVA_VERSION_OVERRIDE_PROP, System.getProperty("java.version"));
+            // e.g.: 1.7.5_30
+            value = ValidateUtils.checkNotNullAndNotEmpty(value, "No configured Java version value").replace('_', '.');
+            // clean up any non-digits - in case something like 1.6.8_25-b323
+            for (int index = 0; index < value.length(); index++) {
+                char ch = value.charAt(index);
+                if ((ch == '.') || ((ch >= '0') && (ch <= '9'))) {
+                    continue;
+                }
+
+                value = value.substring(0, index);
+                break;
+            }
+
+            version = ValidateUtils.checkNotNull(VersionInfo.parse(value), "No version parsed for %s", value);
+            JAVA_VERSION_HOLDER.set(version);
+        }
+
+        return version;
+    }
+
+    /**
+     * Set programmatically the reported Java version
+     * @param version The version - if {@code null} then it will be automatically resolved
+     */
+    public static void setJavaVersion(VersionInfo version) {
+        synchronized (JAVA_VERSION_HOLDER) {
+            JAVA_VERSION_HOLDER.set(version);
+        }
+    }
+
+    /**
+     * @param path The original path
+     * @return A path that can be compared with another one where case
+     * sensitivity of the underlying O/S has been taken into account -
+     * never {@code null}
+     */
+    public static String getComparablePath(String path) {
+        String p = (path == null) ? "" : path;
+        return isWin32() ? p.toLowerCase() : p;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java
new file mode 100644
index 0000000..3075eb3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Readable {
+
+    int available();
+
+    void getRawBytes(byte[] data, int offset, int len);
+
+    /**
+     * Wrap a {@link ByteBuffer} as a {@link Readable} instance
+     *
+     * @param buffer The {@link ByteBuffer} to wrap - never {@code null}
+     * @return The {@link Readable} wrapper
+     */
+    static Readable readable(ByteBuffer buffer) {
+        Objects.requireNonNull(buffer, "No buffer to wrap");
+        return new Readable() {
+            @Override
+            public int available() {
+                return buffer.remaining();
+            }
+
+            @Override
+            public void getRawBytes(byte[] data, int offset, int len) {
+                buffer.get(data, offset, len);
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
new file mode 100644
index 0000000..84a84f9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class ReflectionUtils {
+    public static final Function<Field, String> FIELD_NAME_EXTRACTOR = f -> (f == null) ? null : f.getName();
+
+    private ReflectionUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static Collection<Field> getMatchingFields(Class<?> clazz, Predicate<? super Field> acceptor) {
+        return GenericUtils.selectMatchingMembers(acceptor, clazz.getFields());
+    }
+
+    public static Collection<Field> getMatchingDeclaredFields(Class<?> clazz, Predicate<? super Field> acceptor) {
+        return GenericUtils.selectMatchingMembers(acceptor, clazz.getDeclaredFields());
+    }
+
+    public static boolean isClassAvailable(ClassLoader cl, String className) {
+        try {
+            cl.loadClass(className);
+            return true;
+        } catch (Throwable ignored) {
+            return false;
+        }
+    }
+}