org.springframework.data.hadoop.util.net.DefaultHostInfoDiscovery.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.data.hadoop.util.net.DefaultHostInfoDiscovery.java

Source

/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed 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.springframework.data.hadoop.util.net;

import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;

import org.apache.commons.net.util.SubnetUtils;
import org.apache.commons.net.util.SubnetUtils.SubnetInfo;

import org.springframework.data.hadoop.util.net.HostInfoDiscovery;
import org.springframework.util.StringUtils;

/**
 * Default implementation of {@link HostInfoDiscovery}.
 * <p>
 * Discovery logic for finding ip address is:
 * <ul>
 * <li>all possible network interfaces are requested
 * <li>for interfaces, filter out point to point if not enabled
 * <li>for interfaces, filter out loopback if not enabled
 * <li>for interfaces, explicit regex patter match is done if pattern is set
 * <li>interfaces are sort by preferred name prefixes, on default "eth" and "en" are sort first
 * <li>interfaces are sorted by their indexes assuming eth0 should be picked over eth1
 * <li>interfaces are checked by their list of ip addresses
 * <li>only ipv4 ip's are taken
 * <li>cidr notation to match if from a network/mask is taken in defined
 * <li>what is left, first found ip is taken
 * </ul>
 *
 * @author Janne Valkealahti
 *
 */
public class DefaultHostInfoDiscovery implements HostInfoDiscovery {

    private String matchIpv4;
    private String matchInterface;
    private List<String> preferInterface = Arrays.asList("eth", "en");
    private boolean pointToPoint = false;
    private boolean loopback = false;

    @Override
    public HostInfo getHostInfo() {
        List<NetworkInterface> interfaces;
        try {
            interfaces = getAllAvailableInterfaces();
        } catch (SocketException e) {
            return null;
        }

        // pre filter candidates
        interfaces = filterInterfaces(interfaces);

        // sort to prepare getting first match
        interfaces = sortInterfaces(interfaces);

        for (NetworkInterface nic : interfaces) {
            List<InetAddress> addresses = new ArrayList<InetAddress>();
            for (InterfaceAddress interfaceAddress : nic.getInterfaceAddresses()) {
                addresses.add(interfaceAddress.getAddress());
            }
            addresses = filterAddresses(addresses);
            if (!addresses.isEmpty()) {
                InetAddress address = addresses.get(0);
                return new HostInfo(address.getHostAddress(), address.getHostName());
            }
        }
        return null;
    }

    /**
     * Sets the match ipv4. Used to match ip address from
     * a network using a cidr notation. For example, "192.168.0.1/24"
     * matches range "192.168.0.1-192.168.0.254", "192.168.0.1/16"
     * matches ranre "192.168.0.1-192.168.255.254" and
     * "10.0.0.1/8" matches range "10.0.0.1-10.255.255.254"
     *
     * @param matchIpv4 the new match ipv4
     */
    public void setMatchIpv4(String matchIpv4) {
        this.matchIpv4 = matchIpv4;
    }

    /**
     * Use interface as a candidate if its name is matching with a
     * given pattern. Default value is is empty.
     *
     * @param matchInterface the new match interface regex patter
     * @see NetworkInterface#getName()
     */
    public void setMatchInterface(String matchInterface) {
        this.matchInterface = matchInterface;
    }

    /**
     * Sets the preferred interfaces. Sort interfaces in such
     * order that interface names prefixed with values found from
     * a list is considered first candidates. Defaults to "eth" and
     * "en" which usually are the ones found from unix systems.
     *
     * @param preferInterface the new preferred interface list
     */
    public void setPreferInterface(List<String> preferInterface) {
        this.preferInterface = preferInterface;
    }

    /**
     * Sets if interfaces marked as point to point should be handled.
     * Point to point nic is usually a vpn tunnel which may not be no
     * use to talk to a host unless communication goes through vpn.
     * Default value is <code>FALSE</code>.
     *
     * @param pointToPoint the new point to point flag
     * @see NetworkInterface#isPointToPoint()
     */
    public void setPointToPoint(boolean pointToPoint) {
        this.pointToPoint = pointToPoint;
    }

    /**
     * Sets if loopback should be discovered.
     * Default value is <code>FALSE</code>.
     *
     * @param loopback the new loopback flag
     */
    public void setLoopback(boolean loopback) {
        this.loopback = loopback;
    }

    protected List<NetworkInterface> getAllAvailableInterfaces() throws SocketException {
        List<NetworkInterface> interfaces = new ArrayList<NetworkInterface>(5);
        for (Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements();) {
            interfaces.add(e.nextElement());
        }
        return interfaces;
    }

    private List<InetAddress> filterAddresses(List<InetAddress> addresses) {
        List<InetAddress> filtered = new ArrayList<InetAddress>();
        for (InetAddress address : addresses) {
            // take only ipv4 addresses whose byte array length is always 4
            boolean match = address.getAddress() != null && address.getAddress().length == 4;

            // check loopback
            if (!loopback && address.isLoopbackAddress()) {
                match = false;
            }

            // match cidr if defined
            if (match && StringUtils.hasText(matchIpv4)) {
                match = matchIpv4(matchIpv4, address.getHostAddress());
            }

            if (match) {
                filtered.add(address);
            }
        }
        return filtered;
    }

    private boolean matchIpv4(String addressMatch, String address) {
        // TODO: should we fork utils from jakarta commons?
        SubnetUtils subnetUtils = new SubnetUtils(addressMatch);
        SubnetInfo info = subnetUtils.getInfo();
        return info.isInRange(address);
    }

    private List<NetworkInterface> filterInterfaces(List<NetworkInterface> interfaces) {
        List<NetworkInterface> filtered = new ArrayList<NetworkInterface>();
        for (NetworkInterface nic : interfaces) {
            boolean match = false;

            try {
                match = pointToPoint && nic.isPointToPoint();
            } catch (SocketException e) {
            }

            try {
                match = !match && loopback && nic.isLoopback();
            } catch (SocketException e) {
            }

            // last, if we didn't match anything, let all pass
            // if matchInterface is not set, otherwise do pattern
            // matching
            if (!match && !StringUtils.hasText(matchInterface)) {
                match = true;
            } else if (StringUtils.hasText(matchInterface)) {
                match = nic.getName().matches(matchInterface);
            }

            if (match) {
                filtered.add(nic);
            }
        }
        return filtered;
    }

    private List<NetworkInterface> sortInterfaces(List<NetworkInterface> interfaces) {
        Collections.sort(interfaces, new NicIndexComparator());
        Collections.sort(interfaces, new NicPreferNameComparator());
        return interfaces;
    }

    /**
     * Comparator to sort with nic index.
     */
    private class NicIndexComparator implements Comparator<NetworkInterface> {

        @Override
        public int compare(NetworkInterface o1, NetworkInterface o2) {
            return Integer.compare(o1.getIndex(), o2.getIndex());
        }
    }

    /**
     * Comparator for nic names preferring a list of give prefixes order
     * to sort those before any other.
     */
    private class NicPreferNameComparator implements Comparator<NetworkInterface> {

        @Override
        public int compare(NetworkInterface o1, NetworkInterface o2) {
            String o1name = o1.getName();
            String o2name = o2.getName();
            if (startWithAny(preferInterface, o1name) && startWithAny(preferInterface, o2name)) {
                return 0;
            } else if (startWithAny(preferInterface, o1name) && !startWithAny(preferInterface, o2name)) {
                return -1;
            } else if (!startWithAny(preferInterface, o1name) && startWithAny(preferInterface, o2name)) {
                return 1;
            } else {
                return o1name.compareTo(o2name);
            }
        }

        private boolean startWithAny(List<String> prefixes, String name) {
            for (String prefix : prefixes) {
                if (name.startsWith(prefix)) {
                    return true;
                }
            }
            return false;
        }
    }

    @Override
    public String toString() {
        return "DefaultHostInfoDiscovery [matchIpv4=" + matchIpv4 + ", matchInterface=" + matchInterface
                + ", preferInterface=" + preferInterface + ", pointToPoint=" + pointToPoint + ", loopback="
                + loopback + "]";
    }

}