Github user afine commented on a diff in the pull request: https://github.com/apache/zookeeper/pull/451#discussion_r168576090 --- Diff: src/java/main/org/apache/zookeeper/client/StaticHostProvider.java --- @@ -47,59 +51,169 @@ private int currentIndex = -1; + // Don't re-resolve on first next() call + private boolean connectedSinceNext = true; + + private Resolver resolver; + /** * Constructs a SimpleHostSet. - * + * * @param serverAddresses * possibly unresolved ZooKeeper server addresses * @throws IllegalArgumentException * if serverAddresses is empty or resolves to an empty list */ public StaticHostProvider(Collection<InetSocketAddress> serverAddresses) { + this.resolver = new Resolver() { + @Override + public InetAddress[] getAllByName(String name) throws UnknownHostException { + return InetAddress.getAllByName(name); + } + }; + init(serverAddresses); + } + + /** + * Introduced for testing purposes. getAllByName() is a static method of InetAddress, therefore cannot be easily mocked. + * By abstraction of Resolver interface we can easily inject a mocked implementation in tests. + * + * @param serverAddresses + * possibly unresolved ZooKeeper server addresses + * @param resolver + * custom resolver implementation + * @throws IllegalArgumentException + * if serverAddresses is empty or resolves to an empty list + */ + public StaticHostProvider(Collection<InetSocketAddress> serverAddresses, Resolver resolver) { + this.resolver = resolver; + init(serverAddresses); + } + + /** + * Common init method for all constructors. + * Resolve all unresolved server addresses, put them in a list and shuffle. + */ + private void init(Collection<InetSocketAddress> serverAddresses) { for (InetSocketAddress address : serverAddresses) { try { - InetAddress ia = address.getAddress(); - InetAddress resolvedAddresses[] = InetAddress.getAllByName((ia != null) ? ia.getHostAddress() : - address.getHostName()); + InetAddress resolvedAddresses[] = this.resolver.getAllByName(getHostString(address)); for (InetAddress resolvedAddress : resolvedAddresses) { - // If hostName is null but the address is not, we can tell that - // the hostName is an literal IP address. Then we can set the host string as the hostname - // safely to avoid reverse DNS lookup. - // As far as i know, the only way to check if the hostName is null is use toString(). - // Both the two implementations of InetAddress are final class, so we can trust the return value of - // the toString() method. - if (resolvedAddress.toString().startsWith("/") - && resolvedAddress.getAddress() != null) { - this.serverAddresses.add( - new InetSocketAddress(InetAddress.getByAddress( - address.getHostName(), - resolvedAddress.getAddress()), - address.getPort())); - } else { - this.serverAddresses.add(new InetSocketAddress(resolvedAddress.getHostAddress(), address.getPort())); - } + this.serverAddresses.add(new InetSocketAddress(resolvedAddress, address.getPort())); } } catch (UnknownHostException e) { LOG.error("Unable to connect to server: {}", address, e); } } - + if (this.serverAddresses.isEmpty()) { throw new IllegalArgumentException( "A HostProvider may not be empty!"); } + Collections.shuffle(this.serverAddresses); } + /** + * Evaluate to a hostname if one is available and otherwise it returns the + * string representation of the IP address. + * + * In Java 7, we have a method getHostString, but earlier versions do not support it. + * This method is to provide a replacement for InetSocketAddress.getHostString(). + * + * @param addr + * @return Hostname string of address parameter + */ + private String getHostString(InetSocketAddress addr) { + String hostString = ""; + + if (addr == null) { + return hostString; + } + if (!addr.isUnresolved()) { + InetAddress ia = addr.getAddress(); + + // If the string starts with '/', then it has no hostname + // and we want to avoid the reverse lookup, so we return + // the string representation of the address. + if (ia.toString().startsWith("/")) { + hostString = ia.getHostAddress(); + } else { + hostString = addr.getHostName(); + } + } else { + // According to the Java 6 documentation, if the hostname is + // unresolved, then the string before the colon is the hostname. + String addrString = addr.toString(); + hostString = addrString.substring(0, addrString.lastIndexOf(':')); + } + + return hostString; + } + public int size() { return serverAddresses.size(); } + // Counts the number of addresses added and removed during + // the last call to next. Used mainly for test purposes. + // See StasticHostProviderTest. + private int nextAdded = 0; --- End diff -- instead of exposing all these metrics, which involve making changes to application logic. Why don't we just expose the `serverAddresses` to the test?
---