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 "comparator" 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 "peeling"
+ * 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 "effective" 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 "accumulate" 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 "unified"
+ * {@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 "user.name" 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 "java.version" 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 "os.name" 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 "(User);" 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 "java.version" 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;
+ }
+ }
+}