All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kiwiproject.net.KiwiInternetAddresses Maven / Gradle / Ivy

Go to download

Kiwi is a utility library. We really like Google's Guava, and also use Apache Commons. But if they don't have something we need, and we think it is useful, this is where we put it.

There is a newer version: 4.5.2
Show newest version
package org.kiwiproject.net;

import static com.google.common.base.Strings.nullToEmpty;
import static java.util.stream.Collectors.toList;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.HostAndPort;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.UncheckedIOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * Utilities for {@link InetAddress} and other things related to internet addresses. Note that Google Guava also has
 * the {@link com.google.common.net.InetAddresses} class, so if something is not in here, check there. This set of
 * utilities only contains things that are not in the JDK or Guava.
 * 

* Note that some of the methods accept (e.g. as default value) or return {@link SimpleHostInfo}. This is because * the {@link InetAddress} class has no public constructors and can only be instantiated by its static factory * methods. As a result, if you want to specify your own custom host name and/or IP address as the default value, for * example to use a different host alias or subnet IP, then you must use the methods that work with {@link SimpleHostInfo}. * Otherwise when working with {@link InetAddress} instances, using {@link InetAddress#getLoopbackAddress()} is about the * only other default value you could use. *

* Last, note that {@link SimpleHostInfo} only contains the host name and host address as a string. */ @SuppressWarnings("UnstableApiUsage") // Because Guava's InetAddresses is marked @Beta (but has been there a long time) @UtilityClass @Slf4j public class KiwiInternetAddresses { @VisibleForTesting @Setter(AccessLevel.PACKAGE) static InetAddressFinder addressFinder = new InetAddressFinder(); /** * Get local host as an optional of {@link SimpleHostInfo}. * * @return optional containing local host info, or empty if any error occurred */ public static Optional getLocalHostInfo() { try { var address = addressFinder.getLocalHost(); var hostInfo = SimpleHostInfo.fromInetAddress(address); return Optional.of(hostInfo); } catch (UnknownHostException e) { LOG.warn("Unable to get local host", e); return Optional.empty(); } } /** * Get local host as an optional of {@link InetAddress} * * @return optional containing local host info, or empty if any error occurred */ public static Optional getLocalHostInetAddress() { try { var address = addressFinder.getLocalHost(); return Optional.of(address); } catch (UnknownHostException e) { LOG.warn("Unable to get local host", e); return Optional.empty(); } } /** * Get local host as a {@link SimpleHostInfo}. * * @param defaultValue the default value to use if local host could not be obtained for any reason * @return the local host info, or the specified {@code defaultValue} if any error occurred */ public static SimpleHostInfo getLocalHostInfo(SimpleHostInfo defaultValue) { return getLocalHostInfo().orElse(defaultValue); } /** * Get local host as a {@link InetAddress}. * * @param defaultValue the default value to use if local host address could not be obtained for any reason * @return the local host info, or the specified {@code defaultValue} if any error occurred */ public static InetAddress getLocalHostInetAddress(InetAddress defaultValue) { return getLocalHostInetAddress().orElse(defaultValue); } /** * Get local host as a {@link SimpleHostInfo}. * * @param defaultValueSupplier supplier for the default value to use if local host could not be obtained * for any reason * @return the local host info, or the value obtained from the {@code defaultValueSupplier} * if any error occurred */ public static SimpleHostInfo getLocalHostInfo(Supplier defaultValueSupplier) { return getLocalHostInfo().orElseGet(defaultValueSupplier); } /** * Get local host as a {@link InetAddress}. * * @param defaultValueSupplier supplier for the default value to use if local host address could not be obtained * for any reason * @return the local host info, or the value obtained from the {@code defaultValueSupplier} * if any error occurred */ public static InetAddress getLocalHostInetAddress(Supplier defaultValueSupplier) { return getLocalHostInetAddress().orElseGet(defaultValueSupplier); } /** * Returns the port in the given URL, or an empty optional otherwise. * * @param url a URL * @return an optional containing the port, or empty * @throws UncheckedMalformedURLException if the given string URL is not valid * @see URL#URL(String) */ public static Optional portFrom(String url) { try { return portFrom(new URL(nullToEmpty(url))); } catch (MalformedURLException e) { throw new UncheckedMalformedURLException(e); } } /** * Returns the port in the given URL, or an empty optional otherwise. * * @param url a URL * @return an optional containing the port, or empty if the URL did not have a port * @implNote The {@link URL#getPort()} returns {@code -1} if there is no port. Here we change that to an {@link Optional} * @see URL#getPort() */ public static Optional portFrom(URL url) { int port = url.getPort(); return port == -1 ? Optional.empty() : Optional.of(port); } /** * Returns a {@link HostAndPort} from the given URL. * * @param url a URL as a string * @return HostAndPort instance */ public static HostAndPort hostAndPortFrom(String url) { try { return hostAndPortFrom(new URL(nullToEmpty(url))); } catch (MalformedURLException e) { throw new UncheckedMalformedURLException(e); } } /** * Returns a {@link HostAndPort} from the given URL. * * @param url a URL * @return HostAndPort instance */ public static HostAndPort hostAndPortFrom(URL url) { if (url.getPort() == -1) { return HostAndPort.fromHost(url.getHost()); } return HostAndPort.fromParts(url.getHost(), url.getPort()); } /** * Finds the first IP address on the machine that matches one of the given subnet CIDRs or returns null if not found. * The {@link IpScheme} is used to filter the IP addresses by IPv4 or IPv6. * * @param subnetCidrs A list of CIDRs used to match against the machine's IP addresses. * @param ipScheme Whether to filter by IPv4 or IPv6. * @return the first found matching IP address or null. */ public static String findFirstMatchingAddressOrNull(List subnetCidrs, IpScheme ipScheme) { return findFirstMatchingAddress(subnetCidrs, ipScheme).orElse(null); } /** * Finds the first IP address from a given list of ip addresses that matches one of the given subnet CIDRs or returns * null if not found. * * @param subnetCidrs A list of CIDRs used to match against the machine's IP addresses. * @param ipAddresses A list of IP addresses to search for a match. * @return the first found matching IP address or null. */ public static String findFirstMatchingAddressOrNull(List subnetCidrs, List ipAddresses) { return findFirstMatchingAddress(subnetCidrs, ipAddresses).orElse(null); } /** * Finds the first IP address on the machine that matches one of the given subnet CIDRs or throws * IllegalStateException if not found. The {@link IpScheme} is used to filter the IP addresses by IPv4 or IPv6. * * @param subnetCidrs A list of CIDRs used to match against the machine's IP addresses. * @param ipScheme Whether to filter by IPv4 or IPv6. * @return the first found matching IP address. * @throws IllegalStateException if a matching IP address can not be found. */ public static String findFirstMatchingAddressOrThrow(List subnetCidrs, IpScheme ipScheme) { return findFirstMatchingAddress(subnetCidrs, ipScheme).orElseThrow(() -> new IllegalStateException("Unable to find IP address matching a valid subnet CIDR in: " + subnetCidrs)); } /** * Finds the first IP address on the machine that matches one of the given subnet CIDRs or throws * IllegalStateException if not found. * * @param subnetCidrs A list of CIDRs used to match against the machine's IP addresses. * @param ipAddresses A list of IP addresses to search for a match. * @return the first found matching IP address. * @throws IllegalStateException if a matching IP address can not be found. */ public static String findFirstMatchingAddressOrThrow(List subnetCidrs, List ipAddresses) { return findFirstMatchingAddress(subnetCidrs, ipAddresses).orElseThrow(() -> new IllegalStateException("Unable to find IP address matching a valid subnet CIDR in: " + subnetCidrs)); } /** * Attempts to find the first IP address on the machine that matches one of the given subnet CIDRs. The * {@link IpScheme} is used to filter the IP addresses by IPv4 or IPv6. * * @param subnetCidrs A list of CIDRs used to match against the machine's IP addresses. * @param ipScheme Whether to filter by IPv4 or IPv6 * @return an Optional containing the first found matching IP address or {@link Optional#empty()} if not found. */ public static Optional findFirstMatchingAddress(List subnetCidrs, IpScheme ipScheme) { var ipAddresses = getEnumeratedNetworkAddresses(ipScheme); return findFirstMatchingAddress(subnetCidrs, ipAddresses); } /** * Attempts to find the first IP address from a given list of ip addresses that matches one of the given subnet CIDRs. * * @param subnetCidrs A list of CIDRs used to match against the machine's IP addresses. * @param ipAddresses A list of IP addresses to search for a match. * @return an Optional containing the first found matching IP address or {@link Optional#empty()} if not found. */ public static Optional findFirstMatchingAddress(List subnetCidrs, List ipAddresses) { return findMatchingAddresses(subnetCidrs, ipAddresses) .stream() .findFirst(); } /** * Finds all IP addresses on the machine that matches one of the given subnet CIDRs. The {@link IpScheme} is used to * filter the IP addresses by IPv4 or IPv6. * * @param subnetCidrs A list of CIDRs used to match against the machine's IP addresses. * @param ipScheme Whether to filter by IPv4 or IPv6 * @return a list of matching IP addresses. */ public static List findMatchingAddresses(List subnetCidrs, IpScheme ipScheme) { var ipAddresses = getEnumeratedNetworkAddresses(ipScheme); return findMatchingAddresses(subnetCidrs, ipAddresses); } /** * Finds all IP addresses from a given list of ip addresses that matches one of the given subnet CIDRs. * * @param subnetCidrs A list of CIDRs used to match against the machine's IP addresses. * @param ipAddresses A list of IP addresses to search for a match. * @return a list of matching IP addresses. */ public static List findMatchingAddresses(List subnetCidrs, List ipAddresses) { return subnetCidrs.stream() .map(CidrRange::new) .map(cidr -> ipAddresses.stream().filter(cidr::isInRange).findFirst()) .flatMap(Optional::stream) .collect(toList()); } @VisibleForTesting static List getEnumeratedNetworkAddresses(IpScheme ipScheme) { try { var interfaces = NetworkInterface.getNetworkInterfaces(); return Collections.list(interfaces) .stream() .map(networkInterface -> getInterfaceIps(networkInterface, ipScheme)) .flatMap(List::stream) .collect(toList()); } catch (SocketException e) { throw new UncheckedIOException("Error getting enumeration of network interfaces.", e); } } private static List getInterfaceIps(NetworkInterface networkInterface, IpScheme ipScheme) { var addresses = networkInterface.getInetAddresses(); return Collections.list(addresses) .stream() .filter(address -> ipScheme.getInetAddressClass().isAssignableFrom(address.getClass())) .map(InetAddress::getHostAddress) .collect(toList()); } /** * Simple value class encapsulating a host name and IP address */ @Getter @EqualsAndHashCode @Builder @ToString public static class SimpleHostInfo { private final String hostName; private final String ipAddr; /** * Create instance from an {@link InetAddress} * * @param address the internet address * @return a new SimpleHostInfo instance */ public static SimpleHostInfo fromInetAddress(InetAddress address) { return from(address.getHostName(), address.getHostAddress()); } /** * Create instance from host name and ip address * * @param hostName the host name * @param ipAddr the IP address * @return a new SimpleHostInfo instance */ public static SimpleHostInfo from(String hostName, String ipAddr) { return SimpleHostInfo.builder() .hostName(hostName) .ipAddr(ipAddr) .build(); } } @VisibleForTesting static class InetAddressFinder { InetAddress getLocalHost() throws UnknownHostException { return InetAddress.getLocalHost(); } } /** * Enum that defines the IP scheme to use when looking up a machine's IP addresses. */ public enum IpScheme { IPV6(Inet6Address.class), IPV4(Inet4Address.class); @Getter private final Class inetAddressClass; IpScheme(Class inetAddressClass) { this.inetAddressClass = inetAddressClass; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy