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:37 UTC

[27/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-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
deleted file mode 100644
index da5f687..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.client.config.hosts;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * Monitors the {@code ~/.ssh/config} file of the user currently running
- * the client, re-loading it if necessary. It also (optionally) enforces
- * the same permissions regime as {@code OpenSSH}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultConfigFileHostEntryResolver extends ConfigFileHostEntryResolver {
-    /**
-     * The default instance that enforces the same permissions regime as {@code OpenSSH}
-     */
-    public static final DefaultConfigFileHostEntryResolver INSTANCE = new DefaultConfigFileHostEntryResolver(true);
-
-    private final boolean strict;
-
-    /**
-     * @param strict If {@code true} then makes sure that the containing folder
-     *               has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
-     *               does not check these permissions
-     * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
-     */
-    public DefaultConfigFileHostEntryResolver(boolean strict) {
-        this(HostConfigEntry.getDefaultHostConfigFile(), strict);
-    }
-
-    public DefaultConfigFileHostEntryResolver(File file, boolean strict) {
-        this(Objects.requireNonNull(file, "No file provided").toPath(), strict, IoUtils.getLinkOptions(true));
-    }
-
-    public DefaultConfigFileHostEntryResolver(Path path, boolean strict, LinkOption... options) {
-        super(path, options);
-        this.strict = strict;
-    }
-
-    /**
-     * @return If {@code true} then makes sure that the containing folder
-     * has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
-     * does not check these permissions
-     * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
-     */
-    public final boolean isStrict() {
-        return strict;
-    }
-
-    @Override
-    protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username) throws IOException {
-        if (isStrict()) {
-            if (log.isDebugEnabled()) {
-                log.debug("reloadHostConfigEntries({}@{}:{}) check permissions of {}", username, host, port, path);
-            }
-
-            Map.Entry<String, ?> violation = validateStrictConfigFilePermissions(path);
-            if (violation != null) {
-                log.warn("reloadHostConfigEntries({}@{}:{}) invalid file={} permissions: {}",
-                         username, host, port, path, violation.getKey());
-                updateReloadAttributes();
-                return Collections.emptyList();
-            }
-        }
-
-        return super.reloadHostConfigEntries(path, host, port, username);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
deleted file mode 100644
index f401cee..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
+++ /dev/null
@@ -1,1169 +0,0 @@
-/*
- * 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.client.config.hosts;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.StreamCorruptedException;
-import java.io.Writer;
-import java.net.InetAddress;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.auth.MutableUserHolder;
-import org.apache.sshd.common.config.SshConfigFileReader;
-import org.apache.sshd.common.config.keys.IdentityUtils;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseOutputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
-
-/**
- * Represents an entry in the client's configuration file as defined by
- * the <A HREF="http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config">configuration
- * file format</A>
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class HostConfigEntry extends HostPatternsHolder implements MutableUserHolder {
-    /**
-     * Standard OpenSSH config file name
-     */
-    public static final String STD_CONFIG_FILENAME = "config";
-
-    public static final String HOST_CONFIG_PROP = "Host";
-    public static final String HOST_NAME_CONFIG_PROP = "HostName";
-    public static final String PORT_CONFIG_PROP = SshConfigFileReader.PORT_CONFIG_PROP;
-    public static final String USER_CONFIG_PROP = "User";
-    public static final String IDENTITY_FILE_CONFIG_PROP = "IdentityFile";
-    /**
-     * Use only the identities specified in the host entry (if any)
-     */
-    public static final String EXCLUSIVE_IDENTITIES_CONFIG_PROP = "IdentitiesOnly";
-    public static final boolean DEFAULT_EXCLUSIVE_IDENTITIES = false;
-
-    /**
-     * A case <U>insensitive</U> {@link Set} of the properties that receive special handling
-     */
-    public static final Set<String> EXPLICIT_PROPERTIES =
-            Collections.unmodifiableSet(
-                    GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER,
-                            HOST_CONFIG_PROP, HOST_NAME_CONFIG_PROP, PORT_CONFIG_PROP,
-                            USER_CONFIG_PROP, IDENTITY_FILE_CONFIG_PROP, EXCLUSIVE_IDENTITIES_CONFIG_PROP
-                        ));
-
-    public static final String MULTI_VALUE_SEPARATORS = " ,";
-
-    public static final char HOME_TILDE_CHAR = '~';
-    public static final char PATH_MACRO_CHAR = '%';
-    public static final char LOCAL_HOME_MACRO = 'd';
-    public static final char LOCAL_USER_MACRO = 'u';
-    public static final char LOCAL_HOST_MACRO = 'l';
-    public static final char REMOTE_HOST_MACRO = 'h';
-    public static final char REMOTE_USER_MACRO = 'r';
-    // Extra - not part of the standard
-    public static final char REMOTE_PORT_MACRO = 'p';
-
-    private static final class LazyDefaultConfigFileHolder {
-        private static final Path CONFIG_FILE =
-            PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_CONFIG_FILENAME);
-
-        private LazyDefaultConfigFileHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    private String host;
-    private String hostName;
-    private int port;
-    private String username;
-    private Boolean exclusiveIdentites;
-    private Collection<String> identities = Collections.emptyList();
-    private Map<String, String> properties = Collections.emptyMap();
-
-    public HostConfigEntry() {
-        super();
-    }
-
-    public HostConfigEntry(String pattern, String host, int port, String username) {
-        setHost(pattern);
-        setHostName(host);
-        setPort(port);
-        setUsername(username);
-    }
-
-    /**
-     * @return The <U>pattern(s)</U> represented by this entry
-     */
-    public String getHost() {
-        return host;
-    }
-
-    public void setHost(String host) {
-        this.host = host;
-        setPatterns(parsePatterns(parseConfigValue(host)));
-    }
-
-    public void setHost(Collection<String> patterns) {
-        this.host = GenericUtils.join(ValidateUtils.checkNotNullAndNotEmpty(patterns, "No patterns"), ',');
-        setPatterns(parsePatterns(patterns));
-    }
-
-    /**
-     * @return The effective host name to connect to if the pattern matches
-     */
-    public String getHostName() {
-        return hostName;
-    }
-
-    public void setHostName(String hostName) {
-        this.hostName = hostName;
-    }
-
-    public String resolveHostName(String originalHost) {
-        return resolveHostName(originalHost, getHostName());
-    }
-
-    /**
-     * @return A port override - if positive
-     */
-    public int getPort() {
-        return port;
-    }
-
-    public void setPort(int port) {
-        this.port = port;
-    }
-
-    /**
-     * Resolves the effective port to use
-     *
-     * @param originalPort The original requested port
-     * @return If the host entry port is positive, then it is used, otherwise
-     * the original requested port
-     * @see #resolvePort(int, int)
-     */
-    public int resolvePort(int originalPort) {
-        return resolvePort(originalPort, getPort());
-    }
-
-    /**
-     * @return A username override - if not {@code null}/empty
-     */
-    @Override
-    public String getUsername() {
-        return username;
-    }
-
-    @Override
-    public void setUsername(String username) {
-        this.username = username;
-    }
-
-    /**
-     * Resolves the effective username
-     *
-     * @param originalUser The original requested username
-     * @return If the configured host entry username is not {@code null}/empty
-     * then it is used, otherwise the original one.
-     * @see #resolveUsername(String)
-     */
-    public String resolveUsername(String originalUser) {
-        return resolveUsername(originalUser, getUsername());
-    }
-
-    /**
-     * @return The current identities file paths - may be {@code null}/empty
-     */
-    public Collection<String> getIdentities() {
-        return identities;
-    }
-
-    /**
-     * @param file A {@link File} that contains an identity key - never {@code null}
-     */
-    public void addIdentity(File file) {
-        addIdentity(Objects.requireNonNull(file, "No file").toPath());
-    }
-
-    /**
-     * @param path A {@link Path} to a file that contains an identity key
-     * - never {@code null}
-     */
-    public void addIdentity(Path path) {
-        addIdentity(Objects.requireNonNull(path, "No path").toAbsolutePath().normalize().toString());
-    }
-
-    /**
-     * Adds a path to an identity file
-     *
-     * @param id The identity path to add - never {@code null}
-     */
-    public void addIdentity(String id) {
-        String path = ValidateUtils.checkNotNullAndNotEmpty(id, "No identity provided");
-        if (GenericUtils.isEmpty(identities)) {
-            identities = new LinkedList<>();
-        }
-        identities.add(path);
-    }
-
-    public void setIdentities(Collection<String> identities) {
-        this.identities = (identities == null) ? Collections.emptyList() : identities;
-    }
-
-    /**
-     * @return {@code true} if must use only the identities in this entry
-     */
-    public boolean isIdentitiesOnly() {
-        return (exclusiveIdentites == null) ? DEFAULT_EXCLUSIVE_IDENTITIES : exclusiveIdentites;
-    }
-
-    public void setIdentitiesOnly(boolean identitiesOnly) {
-        exclusiveIdentites = identitiesOnly;
-    }
-
-    /**
-     * @return A {@link Map} of extra properties that have been read - may be
-     * {@code null}/empty, or even contain some values that have been parsed
-     * and set as members of the entry (e.g., host, port, etc.). <B>Note:</B>
-     * multi-valued keys use a comma-separated list of values
-     */
-    public Map<String, String> getProperties() {
-        return properties;
-    }
-
-    /**
-     * @param name Property name - never {@code null}/empty
-     * @return Property value or {@code null}  if no such property
-     * @see #getProperty(String, String)
-     */
-    public String getProperty(String name) {
-        return getProperty(name, null);
-    }
-
-    /**
-     * @param name Property name - never {@code null}/empty
-     * @param defaultValue Default value to return if no such property
-     * @return The property value or the default one if no such property
-     */
-    public String getProperty(String name, String defaultValue) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        Map<String, String> props = getProperties();
-        if (GenericUtils.isEmpty(props)) {
-            return null;
-        }
-
-        String value = props.get(key);
-        if (GenericUtils.isEmpty(value)) {
-            return defaultValue;
-        } else {
-            return value;
-        }
-    }
-
-    /**
-     * Updates the values that are <U>not</U> already configured with those
-     * from the global entry
-     *
-     * @param globalEntry The global entry - ignored if {@code null} or
-     * same reference as this entry
-     * @return {@code true} if anything updated
-     */
-    public boolean processGlobalValues(HostConfigEntry globalEntry) {
-        if ((globalEntry == null) || (this == globalEntry)) {
-            return false;
-        }
-
-        boolean modified = false;
-        /*
-         * NOTE !!! DO NOT TRY TO CHANGE THE ORDER OF THE OR-ing AS IT
-         * WOULD CAUSE INVALID CODE EXECUTION
-         */
-        modified = updateGlobalPort(globalEntry.getPort()) || modified;
-        modified = updateGlobalHostName(globalEntry.getHostName()) || modified;
-        modified = updateGlobalUserName(globalEntry.getUsername()) || modified;
-        modified = updateGlobalIdentities(globalEntry.getIdentities()) || modified;
-        modified = updateGlobalIdentityOnly(globalEntry.isIdentitiesOnly()) || modified;
-
-        Map<String, String> updated = updateGlobalProperties(globalEntry.getProperties());
-        modified = (GenericUtils.size(updated) > 0) || modified;
-
-        return modified;
-    }
-
-    /**
-     * Sets all the properties for which no current value exists in the entry
-     *
-     * @param props The global properties - ignored if {@code null}/empty
-     * @return A {@link Map} of the <U>updated</U> properties
-     */
-    public Map<String, String> updateGlobalProperties(Map<String, String> props) {
-        if (GenericUtils.isEmpty(props)) {
-            return Collections.emptyMap();
-        }
-
-        Map<String, String> updated = null;
-        // Cannot use forEach because of the modification of the updated map value (non-final)
-        for (Map.Entry<String, String> pe : props.entrySet()) {
-            String key = pe.getKey();
-            String curValue = getProperty(key);
-            if (GenericUtils.length(curValue) > 0) {
-                continue;
-            }
-
-            String newValue = pe.getValue();
-            setProperty(key, newValue);
-
-            if (updated == null) {
-                updated = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-            }
-
-            updated.put(key, newValue);
-        }
-
-        if (updated == null) {
-            return Collections.emptyMap();
-        } else {
-            return updated;
-        }
-    }
-
-    /**
-     * @param ids Global identities - ignored if {@code null}/empty or already
-     * have configured identities
-     * @return {@code true} if updated identities
-     */
-    public boolean updateGlobalIdentities(Collection<String> ids) {
-        if (GenericUtils.isEmpty(ids) || (GenericUtils.size(getIdentities()) > 0)) {
-            return false;
-        }
-
-        for (String id : ids) {
-            addIdentity(id);
-        }
-
-        return true;
-    }
-
-    /**
-     * @param user The global user name - ignored if {@code null}/empty or
-     * already have a configured user
-     * @return {@code true} if updated the username
-     */
-    public boolean updateGlobalUserName(String user) {
-        if (GenericUtils.isEmpty(user) || (GenericUtils.length(getUsername()) > 0)) {
-            return false;
-        }
-
-        setUsername(user);
-        return true;
-    }
-
-    /**
-     * @param name The global host name - ignored if {@code null}/empty or
-     * already have a configured target host
-     * @return {@code true} if updated the target host
-     */
-    public boolean updateGlobalHostName(String name) {
-        if (GenericUtils.isEmpty(name) || (GenericUtils.length(getHostName()) > 0)) {
-            return false;
-        }
-
-        setHostName(name);
-        return true;
-    }
-
-    /**
-     * @param portValue The global port value - ignored if not positive
-     * or already have a configured port
-     * @return {@code true} if updated the port value
-     */
-    public boolean updateGlobalPort(int portValue) {
-        if ((portValue <= 0) || (getPort() > 0)) {
-            return false;
-        }
-
-        setPort(portValue);
-        return true;
-    }
-
-    /**
-     * @param identitiesOnly Whether to use only the identities in this entry.
-     * Ignored if already set
-     * @return {@code true} if updated the option value
-     */
-    public boolean updateGlobalIdentityOnly(boolean identitiesOnly) {
-        if (exclusiveIdentites != null) {
-            return false;
-        }
-
-        setIdentitiesOnly(identitiesOnly);
-        return true;
-    }
-
-    /**
-     * @param name Property name - never {@code null}/empty
-     * @param valsList The available values for the property
-     * @param ignoreAlreadyInitialized If {@code false} and one of the &quot;known&quot;
-     * properties is encountered then throws an exception
-     * @throws IllegalArgumentException If an existing value is overwritten and
-     * <tt>ignoreAlreadyInitialized</tt> is {@code false} (except for {@link #IDENTITY_FILE_CONFIG_PROP}
-     * which is <U>cumulative</U>
-     * @see #HOST_NAME_CONFIG_PROP
-     * @see #PORT_CONFIG_PROP
-     * @see #USER_CONFIG_PROP
-     * @see #IDENTITY_FILE_CONFIG_PROP
-     */
-    public void processProperty(String name, Collection<String> valsList, boolean ignoreAlreadyInitialized) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        String joinedValue = GenericUtils.join(valsList, ',');
-        appendPropertyValue(key, joinedValue);
-
-        if (HOST_NAME_CONFIG_PROP.equalsIgnoreCase(key)) {
-            ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target hosts N/A: %s", joinedValue);
-
-            String curValue = getHostName();
-            ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
-            setHostName(joinedValue);
-        } else if (PORT_CONFIG_PROP.equalsIgnoreCase(key)) {
-            ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target ports N/A: %s", joinedValue);
-
-            int curValue = getPort();
-            ValidateUtils.checkTrue((curValue <= 0) || ignoreAlreadyInitialized, "Already initialized %s: %d", key, curValue);
-
-            int newValue = Integer.parseInt(joinedValue);
-            ValidateUtils.checkTrue(newValue > 0, "Bad new port value: %d", newValue);
-            setPort(newValue);
-        } else if (USER_CONFIG_PROP.equalsIgnoreCase(key)) {
-            ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target users N/A: %s", joinedValue);
-
-            String curValue = getUsername();
-            ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
-            setUsername(joinedValue);
-        } else if (IDENTITY_FILE_CONFIG_PROP.equalsIgnoreCase(key)) {
-            ValidateUtils.checkTrue(GenericUtils.size(valsList) > 0, "No identity files specified");
-            for (String id : valsList) {
-                addIdentity(id);
-            }
-        } else if (EXCLUSIVE_IDENTITIES_CONFIG_PROP.equalsIgnoreCase(key)) {
-            setIdentitiesOnly(
-                    SshConfigFileReader.parseBooleanValue(
-                            ValidateUtils.checkNotNullAndNotEmpty(joinedValue, "No identities option value")));
-        }
-    }
-
-    /**
-     * Appends a value using a <U>comma</U> to an existing one. If no previous
-     * value then same as calling {@link #setProperty(String, String)}.
-     *
-     * @param name Property name - never {@code null}/empty
-     * @param value The value to be appended - ignored if {@code null}/empty
-     * @return The value <U>before</U> appending - {@code null} if no previous value
-     */
-    public String appendPropertyValue(String name, String value) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        String curVal = getProperty(key);
-        if (GenericUtils.isEmpty(value)) {
-            return curVal;
-        }
-
-        if (GenericUtils.isEmpty(curVal)) {
-            return setProperty(key, value);
-        }
-
-        return setProperty(key, curVal + ',' + value);
-    }
-
-    /**
-     * Sets / Replaces the property value
-     *
-     * @param name Property name - never {@code null}/empty
-     * @param value Property value - if {@code null}/empty then
-     * {@link #removeProperty(String)} is called
-     * @return The previous property value - {@code null} if no such name
-     */
-    public String setProperty(String name, String value) {
-        if (GenericUtils.isEmpty(value)) {
-            return removeProperty(name);
-        }
-
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        if (GenericUtils.isEmpty(properties)) {
-            properties = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        }
-
-        return properties.put(key, value);
-    }
-
-    /**
-     * @param name Property name - never {@code null}/empty
-     * @return The removed property value - {@code null} if no such property name
-     */
-    public String removeProperty(String name) {
-        String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        Map<String, String> props = getProperties();
-        if (GenericUtils.isEmpty(props)) {
-            return null;
-        } else {
-            return props.remove(key);
-        }
-    }
-
-    /**
-     * @param properties The properties to set - if {@code null} then an empty
-     * map is effectively set. <B>Note:</B> it is highly recommended to use a
-     * <U>case insensitive</U> key mapper.
-     */
-    public void setProperties(Map<String, String> properties) {
-        this.properties = (properties == null) ? Collections.emptyMap() : properties;
-    }
-
-    public <A extends Appendable> A append(A sb) throws IOException {
-        sb.append(HOST_CONFIG_PROP).append(' ').append(ValidateUtils.checkNotNullAndNotEmpty(getHost(), "No host pattern")).append(IoUtils.EOL);
-        appendNonEmptyProperty(sb, HOST_NAME_CONFIG_PROP, getHostName());
-        appendNonEmptyPort(sb, PORT_CONFIG_PROP, getPort());
-        appendNonEmptyProperty(sb, USER_CONFIG_PROP, getUsername());
-        appendNonEmptyValues(sb, IDENTITY_FILE_CONFIG_PROP, getIdentities());
-        if (exclusiveIdentites != null) {
-            appendNonEmptyProperty(sb, EXCLUSIVE_IDENTITIES_CONFIG_PROP, SshConfigFileReader.yesNoValueOf(exclusiveIdentites));
-        }
-        appendNonEmptyProperties(sb, getProperties());
-        return sb;
-    }
-
-    @Override
-    public String toString() {
-        return getHost() + ": " + getUsername() + "@" + getHostName() + ":" + getPort();
-    }
-
-    /**
-     * @param <A> The {@link Appendable} type
-     * @param sb The target appender
-     * @param name The property name - never {@code null}/empty
-     * @param port The port value - ignored if non-positive
-     * @return The target appender after having appended (or not) the value
-     * @throws IOException If failed to append the requested data
-     * @see #appendNonEmptyProperty(Appendable, String, Object)
-     */
-    public static <A extends Appendable> A appendNonEmptyPort(A sb, String name, int port) throws IOException {
-        return appendNonEmptyProperty(sb, name, (port > 0) ? Integer.toString(port) : null);
-    }
-
-    /**
-     * Appends the extra properties - while skipping the {@link #EXPLICIT_PROPERTIES} ones
-     *
-     * @param <A> The {@link Appendable} type
-     * @param sb The target appender
-     * @param props The {@link Map} of properties - ignored if {@code null}/empty
-     * @return The target appender after having appended (or not) the value
-     * @throws IOException If failed to append the requested data
-     * @see #appendNonEmptyProperty(Appendable, String, Object)
-     */
-    public static <A extends Appendable> A appendNonEmptyProperties(A sb, Map<String, ?> props) throws IOException {
-        if (GenericUtils.isEmpty(props)) {
-            return sb;
-        }
-
-        // Cannot use forEach because of the IOException being thrown by appendNonEmptyProperty
-        for (Map.Entry<String, ?> pe : props.entrySet()) {
-            String name = pe.getKey();
-            if (EXPLICIT_PROPERTIES.contains(name)) {
-                continue;
-            }
-
-            appendNonEmptyProperty(sb, name, pe.getValue());
-        }
-
-        return sb;
-    }
-
-    /**
-     * @param <A> The {@link Appendable} type
-     * @param sb The target appender
-     * @param name The property name - never {@code null}/empty
-     * @param value The property value - ignored if {@code null}. <B>Note:</B>
-     * if the string representation of the value contains any commas, they are
-     * assumed to indicate a multi-valued property which is broken down to
-     * <U>individual</U> lines - one per value.
-     * @return The target appender after having appended (or not) the value
-     * @throws IOException If failed to append the requested data
-     * @see #appendNonEmptyValues(Appendable, String, Object...)
-     */
-    public static <A extends Appendable> A appendNonEmptyProperty(A sb, String name, Object value) throws IOException {
-        String s = Objects.toString(value, null);
-        String[] vals = GenericUtils.split(s, ',');
-        return appendNonEmptyValues(sb, name, (Object[]) vals);
-    }
-
-    /**
-     * @param <A> The {@link Appendable} type
-     * @param sb The target appender
-     * @param name The property name - never {@code null}/empty
-     * @param values The values to be added - one per line - ignored if {@code null}/empty
-     * @return The target appender after having appended (or not) the value
-     * @throws IOException If failed to append the requested data
-     * @see #appendNonEmptyValues(Appendable, String, Collection)
-     */
-    public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Object... values) throws IOException {
-        return appendNonEmptyValues(sb, name, GenericUtils.isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
-    }
-
-    /**
-     * @param <A> The {@link Appendable} type
-     * @param sb The target appender
-     * @param name The property name - never {@code null}/empty
-     * @param values The values to be added - one per line - ignored if {@code null}/empty
-     * @return The target appender after having appended (or not) the value
-     * @throws IOException If failed to append the requested data
-     */
-    public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Collection<?> values) throws IOException {
-        String k = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
-        if (GenericUtils.isEmpty(values)) {
-            return sb;
-        }
-
-        for (Object v : values) {
-            sb.append("    ").append(k).append(' ').append(Objects.toString(v)).append(IoUtils.EOL);
-        }
-
-        return sb;
-    }
-
-    /**
-     * @param entries The entries - ignored if {@code null}/empty
-     * @return A {@link HostConfigEntryResolver} wrapper using the entries
-     */
-    public static HostConfigEntryResolver toHostConfigEntryResolver(final Collection<? extends HostConfigEntry> entries) {
-        if (GenericUtils.isEmpty(entries)) {
-            return HostConfigEntryResolver.EMPTY;
-        } else {
-            return (host1, port1, username1) -> {
-                List<HostConfigEntry> matches = findMatchingEntries(host1, entries);
-                int numMatches = GenericUtils.size(matches);
-                if (numMatches <= 0) {
-                    return null;
-                }
-
-                HostConfigEntry match = (numMatches == 1) ? matches.get(0) : findBestMatch(matches);
-                if (match == null) {
-                    ValidateUtils.throwIllegalArgumentException("No best match found for %s@%s:%d out of %d matches", username1, host1, port1, numMatches);
-                }
-
-                return normalizeEntry(match, host1, port1, username1);
-            };
-        }
-    }
-
-    /**
-     * @param entry The original entry - ignored if {@code null}
-     * @param host The original host name / address
-     * @param port The original port
-     * @param username The original user name
-     * @return A <U>cloned</U> entry whose values are resolved - including
-     * expanding macros in the identities files
-     * @throws IOException If failed to normalize the entry
-     * @see #resolveHostName(String)
-     * @see #resolvePort(int)
-     * @see #resolveUsername(String)
-     * @see #resolveIdentityFilePath(String, String, int, String)
-     */
-    public static HostConfigEntry normalizeEntry(HostConfigEntry entry, String host, int port, String username) throws IOException {
-        if (entry == null) {
-            return null;
-        }
-
-        HostConfigEntry normal = new HostConfigEntry();
-        normal.setHost(host);
-        normal.setHostName(entry.resolveHostName(host));
-        normal.setPort(entry.resolvePort(port));
-        normal.setUsername(entry.resolveUsername(username));
-
-        Map<String, String> props = entry.getProperties();
-        if (GenericUtils.size(props) > 0) {
-            normal.setProperties(new TreeMap<>(props));
-        }
-
-        Collection<String> ids = entry.getIdentities();
-        if (GenericUtils.isEmpty(ids)) {
-            return normal;
-        }
-
-        normal.setIdentities(Collections.emptyList());  // start fresh
-        for (String id : ids) {
-            String path = resolveIdentityFilePath(id, host, port, username);
-            normal.addIdentity(path);
-        }
-
-        return normal;
-    }
-
-    /**
-     * Resolves the effective target host
-     *
-     * @param originalName The original requested host
-     * @param entryName The configured host
-     * @return If the configured host entry is not {@code null}/empty
-     * then it is used, otherwise the original one.
-     */
-    public static String resolveHostName(String originalName, String entryName) {
-        if (GenericUtils.isEmpty(entryName)) {
-            return originalName;
-        } else {
-            return entryName;
-        }
-    }
-
-    /**
-     * Resolves the effective username
-     *
-     * @param originalUser The original requested username
-     * @param entryUser The configured host entry username
-     * @return If the configured host entry username is not {@code null}/empty
-     * then it is used, otherwise the original one.
-     */
-    public static String resolveUsername(String originalUser, String entryUser) {
-        if (GenericUtils.isEmpty(entryUser)) {
-            return originalUser;
-        } else {
-            return entryUser;
-        }
-    }
-
-    /**
-     * Resolves the effective port to use
-     *
-     * @param originalPort The original requested port
-     * @param entryPort The configured host entry port
-     * @return If the host entry port is positive, then it is used, otherwise
-     * the original requested port
-     */
-    public static int resolvePort(int originalPort, int entryPort) {
-        if (entryPort <= 0) {
-            return originalPort;
-        } else {
-            return entryPort;
-        }
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(File file) throws IOException {
-        return readHostConfigEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(Path path, OpenOption... options) throws IOException {
-        try (InputStream input = Files.newInputStream(path, options)) {
-            return readHostConfigEntries(input, true);
-        }
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(URL url) throws IOException {
-        try (InputStream input = url.openStream()) {
-            return readHostConfigEntries(input, true);
-        }
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(String filePath) throws IOException {
-        try (InputStream inStream = new FileInputStream(filePath)) {
-            return readHostConfigEntries(inStream, true);
-        }
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(InputStream inStream, boolean okToClose) throws IOException {
-        try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
-            return readHostConfigEntries(reader, true);
-        }
-    }
-
-    public static List<HostConfigEntry> readHostConfigEntries(Reader rdr, boolean okToClose) throws IOException {
-        try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
-            return readHostConfigEntries(buf);
-        }
-    }
-
-    /**
-     * Reads configuration entries
-     *
-     * @param rdr The {@link BufferedReader} to use
-     * @return The {@link List} of read {@link HostConfigEntry}-ies
-     * @throws IOException If failed to parse the read configuration
-     */
-    public static List<HostConfigEntry> readHostConfigEntries(BufferedReader rdr) throws IOException {
-        HostConfigEntry curEntry = null;
-        HostConfigEntry globalEntry = null;
-        List<HostConfigEntry> entries = null;
-
-        int lineNumber = 1;
-        for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
-            line = GenericUtils.replaceWhitespaceAndTrim(line);
-            if (GenericUtils.isEmpty(line)) {
-                continue;
-            }
-
-            int pos = line.indexOf(SshConfigFileReader.COMMENT_CHAR);
-            if (pos == 0) {
-                continue;
-            }
-
-            if (pos > 0) {
-                line = line.substring(0, pos);
-                line = line.trim();
-            }
-
-            /*
-             * Some options use '=', others use ' ' - try both
-             * NOTE: we do not validate the format for each option separately
-             */
-            pos = line.indexOf(' ');
-            if (pos < 0) {
-                pos = line.indexOf('=');
-            }
-
-            if (pos < 0) {
-                throw new StreamCorruptedException("No configuration value delimiter at line " + lineNumber + ": " + line);
-            }
-
-            String key = line.substring(0, pos);
-            String value = line.substring(pos + 1);
-            List<String> valsList = parseConfigValue(value);
-
-            if (HOST_CONFIG_PROP.equalsIgnoreCase(key)) {
-                if (GenericUtils.isEmpty(valsList)) {
-                    throw new StreamCorruptedException("Missing host pattern(s) at line " + lineNumber + ": " + line);
-                }
-
-                // If the all-hosts pattern is used, make sure no global section already active
-                for (String name : valsList) {
-                    if (ALL_HOSTS_PATTERN.equalsIgnoreCase(name) && (globalEntry != null)) {
-                        throw new StreamCorruptedException("Overriding the global section with a specific one at line " + lineNumber + ": " + line);
-                    }
-                }
-
-                if (curEntry != null) {
-                    curEntry.processGlobalValues(globalEntry);
-                }
-
-                entries = updateEntriesList(entries, curEntry);
-
-                curEntry = new HostConfigEntry();
-                curEntry.setHost(valsList);
-            } else if (curEntry == null) {
-                // if 1st encountered property is NOT for a specific host, then configuration applies to ALL
-                curEntry = new HostConfigEntry();
-                curEntry.setHost(Collections.singletonList(ALL_HOSTS_PATTERN));
-                globalEntry = curEntry;
-            }
-
-            try {
-                curEntry.processProperty(key, valsList, false);
-            } catch (RuntimeException e) {
-                throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
-                                                 + " to process line #" + lineNumber + " (" + line + ")"
-                                                 + ": " + e.getMessage());
-            }
-        }
-
-        if (curEntry != null) {
-            curEntry.processGlobalValues(globalEntry);
-        }
-
-        entries = updateEntriesList(entries, curEntry);
-        if (entries == null) {
-            return Collections.emptyList();
-        } else {
-            return entries;
-        }
-    }
-
-    /**
-     * Finds the best match out of the given ones.
-     *
-     * @param matches The available matches - ignored if {@code null}/empty
-     * @return The best match or {@code null} if no matches or no best match found
-     * @see #findBestMatch(Iterator)
-     */
-    public static HostConfigEntry findBestMatch(Collection<? extends HostConfigEntry> matches) {
-        if (GenericUtils.isEmpty(matches)) {
-            return null;
-        } else {
-            return findBestMatch(matches.iterator());
-        }
-    }
-
-    /**
-     * Finds the best match out of the given ones.
-     *
-     * @param matches The available matches - ignored if {@code null}/empty
-     * @return The best match or {@code null} if no matches or no best match found
-     * @see #findBestMatch(Iterator)
-     */
-    public static HostConfigEntry findBestMatch(Iterable<? extends HostConfigEntry> matches) {
-        if (matches == null) {
-            return null;
-        } else {
-            return findBestMatch(matches.iterator());
-        }
-    }
-
-    /**
-     * Finds the best match out of the given ones. The best match is defined as one whose
-     * pattern is as <U>specific</U> as possible (if more than one match is available).
-     * I.e., a non-global match is preferred over global one, and a match with no wildcards
-     * is preferred over one with such a pattern.
-     *
-     * @param matches The available matches - ignored if {@code null}/empty
-     * @return The best match or {@code null} if no matches or no best match found
-     * @see #isSpecificHostPattern(String)
-     */
-    public static HostConfigEntry findBestMatch(Iterator<? extends HostConfigEntry> matches) {
-        if ((matches == null) || (!matches.hasNext())) {
-            return null;
-        }
-
-        HostConfigEntry candidate = matches.next();
-        int wildcardMatches = 0;
-        while (matches.hasNext()) {
-            HostConfigEntry entry = matches.next();
-            String entryPattern = entry.getHost();
-            String candidatePattern = candidate.getHost();
-            // prefer non-global entry over global entry
-            if (ALL_HOSTS_PATTERN.equalsIgnoreCase(candidatePattern)) {
-                // unlikely, but handle it
-                if (ALL_HOSTS_PATTERN.equalsIgnoreCase(entryPattern)) {
-                    wildcardMatches++;
-                } else {
-                    candidate = entry;
-                    wildcardMatches = 0;
-                }
-                continue;
-            }
-
-            if (isSpecificHostPattern(entryPattern)) {
-                // if both are specific then no best match
-                if (isSpecificHostPattern(candidatePattern)) {
-                    return null;
-                }
-
-                candidate = entry;
-                wildcardMatches = 0;
-                continue;
-            }
-
-            wildcardMatches++;
-        }
-
-        String candidatePattern = candidate.getHost();
-        // best match either has specific host or no wildcard matches
-        if ((wildcardMatches <= 0) || (isSpecificHostPattern(candidatePattern))) {
-            return candidate;
-        }
-
-        return null;
-    }
-
-    public static List<HostConfigEntry> updateEntriesList(List<HostConfigEntry> entries, HostConfigEntry curEntry) {
-        if (curEntry == null) {
-            return entries;
-        }
-
-        if (entries == null) {
-            entries = new ArrayList<>();
-        }
-
-        entries.add(curEntry);
-        return entries;
-    }
-
-    public static void writeHostConfigEntries(File file, Collection<? extends HostConfigEntry> entries) throws IOException {
-        writeHostConfigEntries(Objects.requireNonNull(file, "No file").toPath(), entries, IoUtils.EMPTY_OPEN_OPTIONS);
-    }
-
-    public static void writeHostConfigEntries(Path path, Collection<? extends HostConfigEntry> entries, OpenOption... options) throws IOException {
-        try (OutputStream outputStream = Files.newOutputStream(path, options)) {
-            writeHostConfigEntries(outputStream, true, entries);
-        }
-    }
-
-    public static void writeHostConfigEntries(OutputStream outputStream, boolean okToClose, Collection<? extends HostConfigEntry> entries) throws IOException {
-        if (GenericUtils.isEmpty(entries)) {
-            return;
-        }
-
-        try (Writer w = new OutputStreamWriter(NoCloseOutputStream.resolveOutputStream(outputStream, okToClose), StandardCharsets.UTF_8)) {
-            appendHostConfigEntries(w, entries);
-        }
-    }
-
-    public static <A extends Appendable> A appendHostConfigEntries(A sb, Collection<? extends HostConfigEntry> entries) throws IOException {
-        if (GenericUtils.isEmpty(entries)) {
-            return sb;
-        }
-
-        for (HostConfigEntry entry : entries) {
-            entry.append(sb);
-        }
-
-        return sb;
-    }
-
-    /**
-     * Checks if this is a multi-value - allow space and comma
-     *
-     * @param value The value - ignored if {@code null}/empty (after trimming)
-     * @return A {@link List} of the encountered values
-     */
-    public static List<String> parseConfigValue(String value) {
-        String s = GenericUtils.replaceWhitespaceAndTrim(value);
-        if (GenericUtils.isEmpty(s)) {
-            return Collections.emptyList();
-        }
-
-        for (int index = 0; index < MULTI_VALUE_SEPARATORS.length(); index++) {
-            char sep = MULTI_VALUE_SEPARATORS.charAt(index);
-            int pos = s.indexOf(sep);
-            if (pos >= 0) {
-                String[] vals = GenericUtils.split(s, sep);
-                if (GenericUtils.isEmpty(vals)) {
-                    return Collections.emptyList();
-                } else {
-                    return Arrays.asList(vals);
-                }
-            }
-        }
-
-        // this point is reached if no separators found
-        return Collections.singletonList(s);
-    }
-
-    // The file name may use the tilde syntax to refer to a user’s home directory or one of the following escape characters:
-    // '%d' (local user's home directory), '%u' (local user name), '%l' (local host name), '%h' (remote host name) or '%r' (remote user name).
-    public static String resolveIdentityFilePath(String id, String host, int port, String username) throws IOException {
-        if (GenericUtils.isEmpty(id)) {
-            return id;
-        }
-
-        String path = id.replace('/', File.separatorChar);  // make sure all separators are local
-        String[] elements = GenericUtils.split(path, File.separatorChar);
-        StringBuilder sb = new StringBuilder(path.length() + Long.SIZE);
-        for (int index = 0; index < elements.length; index++) {
-            String elem = elements[index];
-            if (index > 0) {
-                sb.append(File.separatorChar);
-            }
-
-            for (int curPos = 0; curPos < elem.length(); curPos++) {
-                char ch = elem.charAt(curPos);
-                if (ch == HOME_TILDE_CHAR) {
-                    ValidateUtils.checkTrue((curPos == 0) && (index == 0), "Home tilde must be first: %s", id);
-                    appendUserHome(sb);
-                } else if (ch == PATH_MACRO_CHAR) {
-                    curPos++;
-                    ValidateUtils.checkTrue(curPos < elem.length(), "Missing macro modifier in %s", id);
-                    ch = elem.charAt(curPos);
-                    switch(ch) {
-                        case PATH_MACRO_CHAR:
-                            sb.append(ch);
-                            break;
-                        case LOCAL_HOME_MACRO:
-                            ValidateUtils.checkTrue((curPos == 1) && (index == 0), "Home macro must be first: %s", id);
-                            appendUserHome(sb);
-                            break;
-                        case LOCAL_USER_MACRO:
-                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(OsUtils.getCurrentUser(), "No local user name value"));
-                            break;
-                        case LOCAL_HOST_MACRO: {
-                            InetAddress address = Objects.requireNonNull(InetAddress.getLocalHost(), "No local address");
-                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(address.getHostName(), "No local name"));
-                            break;
-                        }
-                        case REMOTE_HOST_MACRO:
-                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(host, "No remote host provided"));
-                            break;
-                        case REMOTE_USER_MACRO:
-                            sb.append(ValidateUtils.checkNotNullAndNotEmpty(username, "No remote user provided"));
-                            break;
-                        case REMOTE_PORT_MACRO:
-                            ValidateUtils.checkTrue(port > 0, "Bad remote port value: %d", port);
-                            sb.append(port);
-                            break;
-                        default:
-                            ValidateUtils.throwIllegalArgumentException("Bad modifier '%s' in %s", String.valueOf(ch), id);
-                    }
-                } else {
-                    sb.append(ch);
-                }
-            }
-        }
-
-        return sb.toString();
-    }
-
-    public static StringBuilder appendUserHome(StringBuilder sb) {
-        return appendUserHome(sb, IdentityUtils.getUserHomeFolder());
-    }
-
-    public static StringBuilder appendUserHome(StringBuilder sb, Path userHome) {
-        return appendUserHome(sb, Objects.requireNonNull(userHome, "No user home folder").toString());
-    }
-
-    public static StringBuilder appendUserHome(StringBuilder sb, String userHome) {
-        if (GenericUtils.isEmpty(userHome)) {
-            return sb;
-        }
-
-        sb.append(userHome);
-        // strip any ending separator since we add our own
-        int len = sb.length();
-        if (sb.charAt(len - 1) == File.separatorChar) {
-            sb.setLength(len - 1);
-        }
-
-        return sb;
-    }
-
-    /**
-     * @return The default {@link Path} location of the OpenSSH hosts entries configuration file
-     */
-    @SuppressWarnings("synthetic-access")
-    public static Path getDefaultHostConfigFile() {
-        return LazyDefaultConfigFileHolder.CONFIG_FILE;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
deleted file mode 100644
index a07cfcf..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.client.config.hosts;
-
-import java.io.IOException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface HostConfigEntryResolver {
-
-    /**
-     * An &quot;empty&quot; implementation that does not resolve any entry
-     */
-    HostConfigEntryResolver EMPTY = new HostConfigEntryResolver() {
-        @Override
-        public HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException {
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            return "EMPTY";
-        }
-    };
-
-    /**
-     * Invoked when creating a new client session in order to allow for overriding
-     * of the original parameters
-     *
-     * @param host The requested host - never {@code null}/empty
-     * @param port The requested port
-     * @param username The requested username
-     * @return A {@link HostConfigEntry} for the actual target - {@code null} if use
-     * original parameters. <B>Note:</B> if any identity files are attached to the
-     * configuration then they must point to <U>existing</U> locations. This means
-     * that any macros such as <code>~, %d, %h</code>, etc. must be resolved <U>prior</U>
-     * to returning the value
-     * @throws IOException If failed to resolve the configuration
-     */
-    HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
deleted file mode 100644
index 20d682f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.client.config.hosts;
-
-import java.util.regex.Pattern;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Represents a pattern definition in the <U>known_hosts</U> file
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#About_the_Contents_of_the_known_hosts_Files">
- * OpenSSH cookbook - About the Contents of the known hosts Files</A>
- */
-public class HostPatternValue {
-    private Pattern pattern;
-    private int port;
-    private boolean negated;
-
-    public HostPatternValue() {
-        super();
-    }
-
-    public HostPatternValue(Pattern pattern, boolean negated) {
-        this(pattern, 0, negated);
-    }
-
-    public HostPatternValue(Pattern pattern, int port, boolean negated) {
-        this.pattern = pattern;
-        this.port = port;
-        this.negated = negated;
-    }
-
-    public Pattern getPattern() {
-        return pattern;
-    }
-
-    public void setPattern(Pattern pattern) {
-        this.pattern = pattern;
-    }
-
-    public int getPort() {
-        return port;
-    }
-
-    public void setPort(int port) {
-        this.port = port;
-    }
-
-    public boolean isNegated() {
-        return negated;
-    }
-
-    public void setNegated(boolean negated) {
-        this.negated = negated;
-    }
-
-    @Override
-    public String toString() {
-        Pattern p = getPattern();
-        String purePattern = (p == null) ? null : p.pattern();
-        StringBuilder sb = new StringBuilder(GenericUtils.length(purePattern) + Short.SIZE);
-        if (isNegated()) {
-            sb.append(HostPatternsHolder.NEGATION_CHAR_PATTERN);
-        }
-
-        int portValue = getPort();
-        if (portValue > 0) {
-            sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
-        }
-        sb.append(purePattern);
-        if (portValue > 0) {
-            sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM);
-            sb.append(HostPatternsHolder.PORT_VALUE_DELIMITER);
-            sb.append(portValue);
-        }
-
-        return sb.toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
deleted file mode 100644
index 9d90dac..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * 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.client.config.hosts;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class HostPatternsHolder {
-
-    /**
-     * Used in a host pattern to denote zero or more consecutive characters
-     */
-    public static final char WILDCARD_PATTERN = '*';
-    public static final String ALL_HOSTS_PATTERN = String.valueOf(WILDCARD_PATTERN);
-
-    /**
-     * Used in a host pattern to denote any <U>one</U> character
-     */
-    public static final char SINGLE_CHAR_PATTERN = '?';
-
-    /**
-     * Used to negate a host pattern
-     */
-    public static final char NEGATION_CHAR_PATTERN = '!';
-
-    /**
-     * The available pattern characters
-     */
-    public static final String PATTERN_CHARS = new String(new char[]{WILDCARD_PATTERN, SINGLE_CHAR_PATTERN, NEGATION_CHAR_PATTERN});
-
-    /** Port value separator if non-standard port pattern used */
-    public static final char PORT_VALUE_DELIMITER = ':';
-
-    /** Non-standard port specification host pattern enclosure start delimiter */
-    public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM = '[';
-
-    /** Non-standard port specification host pattern enclosure end delimiter */
-    public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM = ']';
-
-    private Collection<HostPatternValue> patterns = new LinkedList<>();
-
-    protected HostPatternsHolder() {
-        super();
-    }
-
-    public Collection<HostPatternValue> getPatterns() {
-        return patterns;
-    }
-
-    public void setPatterns(Collection<HostPatternValue> patterns) {
-        this.patterns = patterns;
-    }
-
-    /**
-     * Checks if a given host name / address matches the entry's host pattern(s)
-     *
-     * @param host The host name / address - ignored if {@code null}/empty
-     * @param port The connection port
-     * @return {@code true} if the name / address matches the pattern(s)
-     * @see #isHostMatch(String, Pattern)
-     */
-    public boolean isHostMatch(String host, int port) {
-        return isHostMatch(host, port, getPatterns());
-    }
-
-    /**
-     * @param pattern The pattern to check - ignored if {@code null}/empty
-     * @return {@code true} if the pattern is not empty and contains no wildcard characters
-     * @see #WILDCARD_PATTERN
-     * @see #SINGLE_CHAR_PATTERN
-     * @see #SINGLE_CHAR_PATTERN
-     */
-    public static boolean isSpecificHostPattern(String pattern) {
-        if (GenericUtils.isEmpty(pattern)) {
-            return false;
-        }
-
-        for (int index = 0; index < PATTERN_CHARS.length(); index++) {
-            char ch = PATTERN_CHARS.charAt(index);
-            if (pattern.indexOf(ch) >= 0) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Locates all the matching entries for a give host name / address
-     *
-     * @param host The host name / address - ignored if {@code null}/empty
-     * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
-     * @return A {@link List} of all the matching entries
-     * @see #isHostMatch(String, int)
-     */
-    public static List<HostConfigEntry> findMatchingEntries(String host, HostConfigEntry... entries) {
-        // TODO in Java-8 use Stream(s) + predicate
-        if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
-            return Collections.emptyList();
-        } else {
-            return findMatchingEntries(host, Arrays.asList(entries));
-        }
-    }
-
-    /**
-     * Locates all the matching entries for a give host name / address
-     *
-     * @param host The host name / address - ignored if {@code null}/empty
-     * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
-     * @return A {@link List} of all the matching entries
-     * @see #isHostMatch(String, int)
-     */
-    public static List<HostConfigEntry> findMatchingEntries(String host, Collection<? extends HostConfigEntry> entries) {
-        // TODO in Java-8 use Stream(s) + predicate
-        if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
-            return Collections.emptyList();
-        }
-
-        List<HostConfigEntry> matches = null;
-        for (HostConfigEntry entry : entries) {
-            if (!entry.isHostMatch(host, 0 /* any port */)) {
-                continue;   // debug breakpoint
-            }
-
-            if (matches == null) {
-                matches = new ArrayList<>(entries.size());  // in case ALL of them match
-            }
-
-            matches.add(entry);
-        }
-
-        if (matches == null) {
-            return Collections.emptyList();
-        } else {
-            return matches;
-        }
-    }
-
-    public static boolean isHostMatch(String host, int port, Collection<HostPatternValue> patterns) {
-        if (GenericUtils.isEmpty(patterns)) {
-            return false;
-        }
-
-        boolean matchFound = false;
-        for (HostPatternValue pv : patterns) {
-            boolean negated = pv.isNegated();
-            /*
-             * If already found a match we are interested only in negations
-             */
-            if (matchFound && (!negated)) {
-                continue;
-            }
-
-            if (!isHostMatch(host, pv.getPattern())) {
-                continue;
-            }
-
-            /*
-             * According to https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5:
-             *
-             *      If a negated entry is matched, then the Host entry is ignored,
-             *      regardless of whether any other patterns on the line match.
-             */
-            if (negated) {
-                return false;
-            }
-
-            int pvPort = pv.getPort();
-            if ((pvPort != 0) && (port != 0) && (pvPort != port)) {
-                continue;
-            }
-
-            matchFound = true;
-        }
-
-        return matchFound;
-    }
-
-    /**
-     * Checks if a given host name / address matches a host pattern
-     *
-     * @param host The host name / address - ignored if {@code null}/empty
-     * @param pattern The host {@link Pattern} - ignored if {@code null}
-     * @return {@code true} if the name / address matches the pattern
-     */
-    public static boolean isHostMatch(String host, Pattern pattern) {
-        if (GenericUtils.isEmpty(host) || (pattern == null)) {
-            return false;
-        }
-
-        Matcher m = pattern.matcher(host);
-        return m.matches();
-    }
-
-    public static List<HostPatternValue> parsePatterns(CharSequence... patterns) {
-        return parsePatterns(GenericUtils.isEmpty(patterns) ? Collections.emptyList() : Arrays.asList(patterns));
-    }
-
-    public static List<HostPatternValue> parsePatterns(Collection<? extends CharSequence> patterns) {
-        if (GenericUtils.isEmpty(patterns)) {
-            return Collections.emptyList();
-        }
-
-        List<HostPatternValue> result = new ArrayList<>(patterns.size());
-        for (CharSequence p : patterns) {
-            result.add(ValidateUtils.checkNotNull(toPattern(p), "No pattern for %s", p));
-        }
-
-        return result;
-    }
-
-    /**
-     * Converts a host pattern string to a regular expression matcher.
-     * <B>Note:</B> pattern matching is <U>case insensitive</U>
-     *
-     * @param patternString The original pattern string - ignored if {@code null}/empty
-     * @return The regular expression matcher {@link Pattern} and the indication
-     * whether it is a negating pattern or not - {@code null} if no original string
-     * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM
-     * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM
-     * @see #WILDCARD_PATTERN
-     * @see #SINGLE_CHAR_PATTERN
-     * @see #NEGATION_CHAR_PATTERN
-     */
-    public static HostPatternValue toPattern(CharSequence patternString) {
-        String pattern = GenericUtils.replaceWhitespaceAndTrim(Objects.toString(patternString, null));
-        if (GenericUtils.isEmpty(pattern)) {
-            return null;
-        }
-
-        int patternLen = pattern.length();
-        int port = 0;
-        // Check if non-standard port value used
-        StringBuilder sb = new StringBuilder(patternLen);
-        if (pattern.charAt(0) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM) {
-            int pos = GenericUtils.lastIndexOf(pattern, HostPatternsHolder.PORT_VALUE_DELIMITER);
-            ValidateUtils.checkTrue(pos > 0, "Missing non-standard port value delimiter in %s", pattern);
-            ValidateUtils.checkTrue(pos < (patternLen - 1), "Missing non-standard port value number in %s", pattern);
-            ValidateUtils.checkTrue(pattern.charAt(pos - 1) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM,
-                "Invalid non-standard port value host pattern enclosure delimiters in %s", pattern);
-
-            String csPort = pattern.substring(pos + 1, patternLen);
-            port = Integer.parseInt(csPort);
-            ValidateUtils.checkTrue((port > 0) && (port <= 0xFFFF), "Invalid non-start port value (%d) in %s", port, pattern);
-
-            pattern = pattern.substring(1, pos - 1);
-            patternLen = pattern.length();
-        }
-
-        boolean negated = false;
-        for (int curPos = 0; curPos < patternLen; curPos++) {
-            char ch = pattern.charAt(curPos);
-            ValidateUtils.checkTrue(isValidPatternChar(ch), "Invalid host pattern char in %s", pattern);
-
-            switch(ch) {
-                case '.':   // need to escape it
-                    sb.append('\\').append(ch);
-                    break;
-                case SINGLE_CHAR_PATTERN:
-                    sb.append('.');
-                    break;
-                case WILDCARD_PATTERN:
-                    sb.append(".*");
-                    break;
-                case NEGATION_CHAR_PATTERN:
-                    ValidateUtils.checkTrue(!negated, "Double negation in %s", pattern);
-                    ValidateUtils.checkTrue(curPos == 0, "Negation must be 1st char: %s", pattern);
-                    negated = true;
-                    break;
-                default:
-                    sb.append(ch);
-            }
-        }
-
-        return new HostPatternValue(Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE), port, negated);
-    }
-
-    /**
-     * Checks if the given character is valid for a host pattern. Valid
-     * characters are:
-     * <UL>
-     *      <LI>A-Z</LI>
-     *      <LI>a-z</LI>
-     *      <LI>0-9</LI>
-     *      <LI>Underscore (_)</LI>
-     *      <LI>Hyphen (-)</LI>
-     *      <LI>Dot (.)</LI>
-     *      <LI>The {@link #WILDCARD_PATTERN}</LI>
-     *      <LI>The {@link #SINGLE_CHAR_PATTERN}</LI>
-     * </UL>
-     *
-     * @param ch The character to validate
-     * @return {@code true} if valid pattern character
-     */
-    public static boolean isValidPatternChar(char ch) {
-        if ((ch <= ' ') || (ch >= 0x7E)) {
-            return false;
-        }
-        if ((ch >= 'a') && (ch <= 'z')) {
-            return true;
-        }
-        if ((ch >= 'A') && (ch <= 'Z')) {
-            return true;
-        }
-        if ((ch >= '0') && (ch <= '9')) {
-            return true;
-        }
-        if ("-_.".indexOf(ch) >= 0) {
-            return true;
-        }
-        return PATTERN_CHARS.indexOf(ch) >= 0;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
deleted file mode 100644
index 2d9a322..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.client.config.hosts;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Objects;
-import java.util.Set;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.mac.BuiltinMacs;
-import org.apache.sshd.common.mac.Mac;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Available digesters for known hosts entries
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum KnownHostDigest implements NamedFactory<Mac> {
-    SHA1("1", BuiltinMacs.hmacsha1);
-
-    public static final Set<KnownHostDigest> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(KnownHostDigest.class));
-
-    private final String name;
-    private final Factory<Mac> factory;
-
-    KnownHostDigest(String name, Factory<Mac> factory) {
-        this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
-        this.factory = Objects.requireNonNull(factory, "No factory");
-    }
-
-    @Override
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public Mac create() {
-        return factory.create();
-    }
-
-    public static KnownHostDigest fromName(String name) {
-        return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
deleted file mode 100644
index 4d4e97a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * 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.client.config.hosts;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StreamCorruptedException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.SshConfigFileReader;
-import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
-
-/**
- * Contains a representation of an entry in the <code>known_hosts</code> file
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://www.manpagez.com/man/8/sshd/">sshd(8) man page</A>
- */
-public class KnownHostEntry extends HostPatternsHolder {
-    /**
-     * Character that denotes that start of a marker
-     */
-    public static final char MARKER_INDICATOR = '@';
-
-    /**
-     * Standard OpenSSH config file name
-     */
-    public static final String STD_HOSTS_FILENAME = "known_hosts";
-
-    private static final class LazyDefaultConfigFileHolder {
-        private static final Path HOSTS_FILE =
-            PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_HOSTS_FILENAME);
-
-        private LazyDefaultConfigFileHolder() {
-            throw new UnsupportedOperationException("No instance allowed");
-        }
-    }
-
-    private String line;
-    private String marker;
-    private AuthorizedKeyEntry keyEntry;
-    private KnownHostHashValue hashedEntry;
-
-    public KnownHostEntry() {
-        super();
-    }
-
-    /**
-     * @param line The original line from which this entry was created
-     */
-    public KnownHostEntry(String line) {
-        this.line = line;
-    }
-
-    /**
-     * @return The original line from which this entry was created
-     */
-    public String getConfigLine() {
-        return line;
-    }
-
-    public void setConfigLine(String line) {
-        this.line = line;
-    }
-
-    public String getMarker() {
-        return marker;
-    }
-
-    public void setMarker(String marker) {
-        this.marker = marker;
-    }
-
-    public AuthorizedKeyEntry getKeyEntry() {
-        return keyEntry;
-    }
-
-    public void setKeyEntry(AuthorizedKeyEntry keyEntry) {
-        this.keyEntry = keyEntry;
-    }
-
-    public KnownHostHashValue getHashedEntry() {
-        return hashedEntry;
-    }
-
-    public void setHashedEntry(KnownHostHashValue hashedEntry) {
-        this.hashedEntry = hashedEntry;
-    }
-
-    @Override
-    public boolean isHostMatch(String host, int port) {
-        if (super.isHostMatch(host, port)) {
-            return true;
-        }
-
-        KnownHostHashValue hash = getHashedEntry();
-        return (hash != null) && hash.isHostMatch(host);
-    }
-
-    @Override
-    public String toString() {
-        return getConfigLine();
-    }
-
-    /**
-     * @return The default {@link Path} location of the OpenSSH known hosts file
-     */
-    @SuppressWarnings("synthetic-access")
-    public static Path getDefaultKnownHostsFile() {
-        return LazyDefaultConfigFileHolder.HOSTS_FILE;
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(File file) throws IOException {
-        return readKnownHostEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(Path path, OpenOption... options) throws IOException {
-        try (InputStream input = Files.newInputStream(path, options)) {
-            return readKnownHostEntries(input, true);
-        }
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(URL url) throws IOException {
-        try (InputStream input = url.openStream()) {
-            return readKnownHostEntries(input, true);
-        }
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(String filePath) throws IOException {
-        try (InputStream inStream = new FileInputStream(filePath)) {
-            return readKnownHostEntries(inStream, true);
-        }
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(InputStream inStream, boolean okToClose) throws IOException {
-        try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
-            return readKnownHostEntries(reader, true);
-        }
-    }
-
-    public static List<KnownHostEntry> readKnownHostEntries(Reader rdr, boolean okToClose) throws IOException {
-        try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
-            return readKnownHostEntries(buf);
-        }
-    }
-
-    /**
-     * Reads configuration entries
-     *
-     * @param rdr The {@link BufferedReader} to use
-     * @return The {@link List} of read {@link KnownHostEntry}-ies
-     * @throws IOException If failed to parse the read configuration
-     */
-    public static List<KnownHostEntry> readKnownHostEntries(BufferedReader rdr) throws IOException {
-        List<KnownHostEntry> entries = null;
-
-        int lineNumber = 1;
-        for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
-            line = GenericUtils.trimToEmpty(line);
-            if (GenericUtils.isEmpty(line)) {
-                continue;
-            }
-
-            int pos = line.indexOf(SshConfigFileReader.COMMENT_CHAR);
-            if (pos == 0) {
-                continue;
-            }
-
-            if (pos > 0) {
-                line = line.substring(0, pos);
-                line = line.trim();
-            }
-
-            try {
-                KnownHostEntry entry = parseKnownHostEntry(line);
-                if (entry == null) {
-                    continue;
-                }
-
-                if (entries == null) {
-                    entries = new ArrayList<>();
-                }
-                entries.add(entry);
-            } catch (RuntimeException | Error e) {   // TODO consider consulting a user callback
-                throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
-                        + " to parse line #" + lineNumber + " '" + line + "': " + e.getMessage());
-            }
-        }
-
-        if (entries == null) {
-            return Collections.emptyList();
-        } else {
-            return entries;
-        }
-    }
-
-    public static KnownHostEntry parseKnownHostEntry(String line) {
-        return parseKnownHostEntry(GenericUtils.isEmpty(line) ? null : new KnownHostEntry(), line);
-    }
-
-    public static <E extends KnownHostEntry> E parseKnownHostEntry(E entry, String data) {
-        String line = GenericUtils.replaceWhitespaceAndTrim(data);
-        if (GenericUtils.isEmpty(line) || (line.charAt(0) == PublicKeyEntry.COMMENT_CHAR)) {
-            return entry;
-        }
-
-        entry.setConfigLine(line);
-
-        if (line.charAt(0) == MARKER_INDICATOR) {
-            int pos = line.indexOf(' ');
-            ValidateUtils.checkTrue(pos > 0, "Missing marker name end delimiter in line=%s", data);
-            ValidateUtils.checkTrue(pos > 1, "No marker name after indicator in line=%s", data);
-            entry.setMarker(line.substring(1, pos));
-            line = line.substring(pos + 1).trim();
-        } else {
-            entry.setMarker(null);
-        }
-
-        int pos = line.indexOf(' ');
-        ValidateUtils.checkTrue(pos > 0, "Missing host patterns end delimiter in line=%s", data);
-        String hostPattern = line.substring(0, pos);
-        line = line.substring(pos + 1).trim();
-
-        if (hostPattern.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) {
-            KnownHostHashValue hash =
-                ValidateUtils.checkNotNull(KnownHostHashValue.parse(hostPattern),
-                    "Failed to extract host hash value from line=%s", data);
-            entry.setHashedEntry(hash);
-            entry.setPatterns(null);
-        } else {
-            entry.setHashedEntry(null);
-            entry.setPatterns(parsePatterns(GenericUtils.split(hostPattern, ',')));
-        }
-
-        AuthorizedKeyEntry key =
-            ValidateUtils.checkNotNull(AuthorizedKeyEntry.parseAuthorizedKeyEntry(line),
-                "No valid key entry recovered from line=%s", data);
-        entry.setKeyEntry(key);
-        return entry;
-    }
-}