Replace infinite Iterable/Iterator with dedicated types

Related: #4065

Motivation:

DnsNameResolver was using a special Iterable/Iterator implementation
that yields an infinite stream of DNS server addresses. However, this
seems to cause confusion.

Modifications:

- Make DnsServerAddresses an abstract class with an abstract stream()
  method that returns DnsServerAddressStream
- Add DnsServerAddressStream that yields DNS server address infinitely
- Remove DnsServerResolver(Group) constructors that accept only a single
  server address, which wasn't very useful in practice
- Extract the DnsServerAddresses implementations to top level
- DnsServerAddresses.defaultAddresses() now returns DnsServerAddresses.
  - Add DnsServerAddresses.defaultAddressList() instead

Result:

Less confusion and more explicitness
This commit is contained in:
Trustin Lee 2015-08-19 11:51:15 +09:00
parent 48ee962d08
commit 99f701fd59
11 changed files with 446 additions and 361 deletions

View File

@ -0,0 +1,46 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project 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 io.netty.resolver.dns;
import java.net.InetSocketAddress;
abstract class DefaultDnsServerAddresses extends DnsServerAddresses {
protected final InetSocketAddress[] addresses;
private final String strVal;
DefaultDnsServerAddresses(String type, InetSocketAddress[] addresses) {
this.addresses = addresses;
final StringBuilder buf = new StringBuilder(type.length() + 2 + addresses.length * 16);
buf.append(type).append('(');
for (InetSocketAddress a: addresses) {
buf.append(a).append(", ");
}
buf.setLength(buf.length() - 2);
buf.append(')');
strVal = buf.toString();
}
@Override
public String toString() {
return strVal;
}
}

View File

@ -41,7 +41,6 @@ import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.InternalThreadLocalMap;
import io.netty.util.internal.OneTimeTask;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SystemPropertyUtil;
@ -91,7 +90,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
private static final DatagramDnsResponseDecoder DECODER = new DatagramDnsResponseDecoder();
private static final DatagramDnsQueryEncoder ENCODER = new DatagramDnsQueryEncoder();
final Iterable<InetSocketAddress> nameServerAddresses;
final DnsServerAddresses nameServerAddresses;
final ChannelFuture bindFuture;
final DatagramChannel ch;
@ -107,11 +106,11 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
*/
final ConcurrentMap<String, List<DnsCacheEntry>> resolveCache = PlatformDependent.newConcurrentHashMap();
private final FastThreadLocal<Iterator<InetSocketAddress>> nameServerAddrIterator =
new FastThreadLocal<Iterator<InetSocketAddress>>() {
private final FastThreadLocal<DnsServerAddressStream> nameServerAddrStream =
new FastThreadLocal<DnsServerAddressStream>() {
@Override
protected Iterator<InetSocketAddress> initialValue() throws Exception {
return nameServerAddresses.iterator();
protected DnsServerAddressStream initialValue() throws Exception {
return nameServerAddresses.stream();
}
};
@ -131,72 +130,18 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
private volatile int maxPayloadSize;
/**
* Creates a new DNS-based name resolver that communicates with a single DNS server.
*
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
* @param channelType the type of the {@link DatagramChannel} to create
* @param nameServerAddress the address of the DNS server
*/
public DnsNameResolver(
EventLoop eventLoop, Class<? extends DatagramChannel> channelType,
InetSocketAddress nameServerAddress) {
this(eventLoop, channelType, ANY_LOCAL_ADDR, nameServerAddress);
}
/**
* Creates a new DNS-based name resolver that communicates with a single DNS server.
*
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
* @param channelType the type of the {@link DatagramChannel} to create
* @param localAddress the local address of the {@link DatagramChannel}
* @param nameServerAddress the address of the DNS server
*/
public DnsNameResolver(
EventLoop eventLoop, Class<? extends DatagramChannel> channelType,
InetSocketAddress localAddress, InetSocketAddress nameServerAddress) {
this(eventLoop, new ReflectiveChannelFactory<DatagramChannel>(channelType), localAddress, nameServerAddress);
}
/**
* Creates a new DNS-based name resolver that communicates with a single DNS server.
*
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
* @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}
* @param nameServerAddress the address of the DNS server
*/
public DnsNameResolver(
EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress nameServerAddress) {
this(eventLoop, channelFactory, ANY_LOCAL_ADDR, nameServerAddress);
}
/**
* Creates a new DNS-based name resolver that communicates with a single DNS server.
*
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
* @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}
* @param localAddress the local address of the {@link DatagramChannel}
* @param nameServerAddress the address of the DNS server
*/
public DnsNameResolver(
EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress localAddress, InetSocketAddress nameServerAddress) {
this(eventLoop, channelFactory, localAddress, DnsServerAddresses.singleton(nameServerAddress));
}
/**
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
*
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
* @param channelType the type of the {@link DatagramChannel} to create
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new {@link Iterator} is
* created from this {@link Iterable} to determine which DNS server should be contacted
* for the next retry in case of failure.
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
* this to determine which DNS server should be contacted for the next retry in case
* of failure.
*/
public DnsNameResolver(
EventLoop eventLoop, Class<? extends DatagramChannel> channelType,
Iterable<InetSocketAddress> nameServerAddresses) {
DnsServerAddresses nameServerAddresses) {
this(eventLoop, channelType, ANY_LOCAL_ADDR, nameServerAddresses);
}
@ -206,13 +151,13 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
* @param channelType the type of the {@link DatagramChannel} to create
* @param localAddress the local address of the {@link DatagramChannel}
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new {@link Iterator} is
* created from this {@link Iterable} to determine which DNS server should be contacted
* for the next retry in case of failure.
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
* this to determine which DNS server should be contacted for the next retry in case
* of failure.
*/
public DnsNameResolver(
EventLoop eventLoop, Class<? extends DatagramChannel> channelType,
InetSocketAddress localAddress, Iterable<InetSocketAddress> nameServerAddresses) {
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) {
this(eventLoop, new ReflectiveChannelFactory<DatagramChannel>(channelType), localAddress, nameServerAddresses);
}
@ -221,13 +166,13 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
*
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
* @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new {@link Iterator} is
* created from this {@link Iterable} to determine which DNS server should be contacted
* for the next retry in case of failure.
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
* this to determine which DNS server should be contacted for the next retry in case
* of failure.
*/
public DnsNameResolver(
EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,
Iterable<InetSocketAddress> nameServerAddresses) {
DnsServerAddresses nameServerAddresses) {
this(eventLoop, channelFactory, ANY_LOCAL_ADDR, nameServerAddresses);
}
@ -237,13 +182,13 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
* @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}
* @param localAddress the local address of the {@link DatagramChannel}
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new {@link Iterator} is
* created from this {@link Iterable} to determine which DNS server should be contacted
* for the next retry in case of failure.
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
* this to determine which DNS server should be contacted for the next retry in case
* of failure.
*/
public DnsNameResolver(
EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress localAddress, Iterable<InetSocketAddress> nameServerAddresses) {
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) {
super(eventLoop);
@ -251,10 +196,6 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
checkNotNull(nameServerAddresses, "nameServerAddresses");
checkNotNull(localAddress, "localAddress");
if (!nameServerAddresses.iterator().hasNext()) {
throw new IllegalArgumentException("nameServerAddresses is empty");
}
this.nameServerAddresses = nameServerAddresses;
bindFuture = newChannel(channelFactory, localAddress);
ch = (DatagramChannel) bindFuture.channel();
@ -922,17 +863,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
}
private InetSocketAddress nextNameServerAddress() {
final InternalThreadLocalMap tlm = InternalThreadLocalMap.get();
Iterator<InetSocketAddress> i = nameServerAddrIterator.get(tlm);
if (i.hasNext()) {
return i.next();
}
// The iterator has reached at its end, create a new iterator.
// We should not reach here if a user created nameServerAddresses via DnsServerAddresses, but just in case ..
i = nameServerAddresses.iterator();
nameServerAddrIterator.set(tlm, i);
return i.next();
return nameServerAddrStream.get().next();
}
/**

View File

@ -67,7 +67,7 @@ abstract class DnsNameResolverContext<T> {
};
private final DnsNameResolver parent;
private final Iterator<InetSocketAddress> nameServerAddrs;
private final DnsServerAddressStream nameServerAddrs;
private final Promise<T> promise;
private final String hostname;
private final boolean traceEnabled;
@ -88,7 +88,7 @@ abstract class DnsNameResolverContext<T> {
this.promise = promise;
this.hostname = hostname;
nameServerAddrs = parent.nameServerAddresses.iterator();
nameServerAddrs = parent.nameServerAddresses.stream();
maxAllowedQueries = parent.maxQueriesPerResolve();
resolveAddressTypes = parent.resolveAddressTypesUnsafe();
traceEnabled = parent.isTraceEnabled();

View File

@ -36,53 +36,27 @@ public final class DnsNameResolverGroup extends NameResolverGroup<InetSocketAddr
private final ChannelFactory<? extends DatagramChannel> channelFactory;
private final InetSocketAddress localAddress;
private final Iterable<InetSocketAddress> nameServerAddresses;
private final DnsServerAddresses nameServerAddresses;
public DnsNameResolverGroup(
Class<? extends DatagramChannel> channelType,
InetSocketAddress nameServerAddress) {
this(channelType, ANY_LOCAL_ADDR, nameServerAddress);
}
public DnsNameResolverGroup(
Class<? extends DatagramChannel> channelType,
InetSocketAddress localAddress, InetSocketAddress nameServerAddress) {
this(new ReflectiveChannelFactory<DatagramChannel>(channelType), localAddress, nameServerAddress);
}
public DnsNameResolverGroup(
ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress nameServerAddress) {
this(channelFactory, ANY_LOCAL_ADDR, nameServerAddress);
}
public DnsNameResolverGroup(
ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress localAddress, InetSocketAddress nameServerAddress) {
this(channelFactory, localAddress, DnsServerAddresses.singleton(nameServerAddress));
}
public DnsNameResolverGroup(
Class<? extends DatagramChannel> channelType,
Iterable<InetSocketAddress> nameServerAddresses) {
Class<? extends DatagramChannel> channelType, DnsServerAddresses nameServerAddresses) {
this(channelType, ANY_LOCAL_ADDR, nameServerAddresses);
}
public DnsNameResolverGroup(
Class<? extends DatagramChannel> channelType,
InetSocketAddress localAddress, Iterable<InetSocketAddress> nameServerAddresses) {
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) {
this(new ReflectiveChannelFactory<DatagramChannel>(channelType), localAddress, nameServerAddresses);
}
public DnsNameResolverGroup(
ChannelFactory<? extends DatagramChannel> channelFactory,
Iterable<InetSocketAddress> nameServerAddresses) {
ChannelFactory<? extends DatagramChannel> channelFactory, DnsServerAddresses nameServerAddresses) {
this(channelFactory, ANY_LOCAL_ADDR, nameServerAddresses);
}
public DnsNameResolverGroup(
ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress localAddress, Iterable<InetSocketAddress> nameServerAddresses) {
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) {
this.channelFactory = channelFactory;
this.localAddress = localAddress;
this.nameServerAddresses = nameServerAddresses;

View File

@ -0,0 +1,29 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project 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 io.netty.resolver.dns;
import java.net.InetSocketAddress;
/**
* An infinite stream of DNS server addresses.
*/
public interface DnsServerAddressStream {
/**
* Retrieves the next DNS server address from the stream.
*/
InetSocketAddress next();
}

View File

@ -16,35 +16,29 @@
package io.netty.resolver.dns;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ThreadLocalRandom;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* Provides a sequence of DNS server addresses to {@link DnsNameResolver}. The {@link Iterator} created by the
* {@link Iterable}s returned by the factory methods of this class is infinite, which means {@link Iterator#hasNext()}
* will never return {@code false} and {@link Iterator#next()} will never raise a {@link NoSuchElementException}.
* Provides an infinite sequence of DNS server addresses to {@link DnsNameResolver}.
*/
@SuppressWarnings("IteratorNextCanNotThrowNoSuchElementException")
public final class DnsServerAddresses {
public abstract class DnsServerAddresses {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsServerAddresses.class);
private static final List<InetSocketAddress> DEFAULT_NAME_SERVER_LIST;
private static final InetSocketAddress[] DEFAULT_NAME_SERVER_ARRAY;
private static final DnsServerAddresses DEFAULT_NAME_SERVERS;
static {
final int DNS_PORT = 53;
@ -57,11 +51,9 @@ public final class DnsServerAddresses {
@SuppressWarnings("unchecked")
final List<String> list = (List<String>) nameservers.invoke(instance);
final int size = list.size();
for (int i = 0; i < size; i ++) {
String dnsAddr = list.get(i);
if (dnsAddr != null) {
defaultNameServers.add(new InetSocketAddress(InetAddress.getByName(dnsAddr), DNS_PORT));
for (String a: list) {
if (a != null) {
defaultNameServers.add(new InetSocketAddress(InetAddress.getByName(a), DNS_PORT));
}
}
} catch (Exception ignore) {
@ -88,106 +80,123 @@ public final class DnsServerAddresses {
DEFAULT_NAME_SERVER_LIST = Collections.unmodifiableList(defaultNameServers);
DEFAULT_NAME_SERVER_ARRAY = defaultNameServers.toArray(new InetSocketAddress[defaultNameServers.size()]);
DEFAULT_NAME_SERVERS = sequential(DEFAULT_NAME_SERVER_ARRAY);
}
/**
* Returns the list of the system DNS server addresses. If it failed to retrieve the list of the system DNS server
* Returns the list of the system DNS server addresses. If it failed to retrieve the list of the system DNS server
* addresses from the environment, it will return {@code "8.8.8.8"} and {@code "8.8.4.4"}, the addresses of the
* Google public DNS servers. Note that the {@code Iterator} of the returned list is not infinite unlike other
* factory methods in this class. To make the returned list infinite, pass it to the other factory method. e.g.
* <pre>
* addresses = {@link #sequential(Iterable) sequential}({@link #defaultAddresses()});
* </pre>
* Google public DNS servers.
*/
public static List<InetSocketAddress> defaultAddresses() {
public static List<InetSocketAddress> defaultAddressList() {
return DEFAULT_NAME_SERVER_LIST;
}
/**
* Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
* the DNS server addresses in a sequential order.
* Returns the {@link DnsServerAddresses} that yields the system DNS server addresses sequentially. If it failed to
* retrieve the list of the system DNS server addresses from the environment, it will use {@code "8.8.8.8"} and
* {@code "8.8.4.4"}, the addresses of the Google public DNS servers.
* <p>
* This method has the same effect with the following code:
* <pre>
* DnsServerAddresses.sequential(DnsServerAddresses.defaultAddressList());
* </pre>
* </p>
*/
public static Iterable<InetSocketAddress> sequential(Iterable<? extends InetSocketAddress> addresses) {
public static DnsServerAddresses defaultAddresses() {
return DEFAULT_NAME_SERVERS;
}
/**
* Returns the {@link DnsServerAddresses} that yields the specified {@code addresses} sequentially. Once the
* last address is yielded, it will start again from the first address.
*/
public static DnsServerAddresses sequential(Iterable<? extends InetSocketAddress> addresses) {
return sequential0(sanitize(addresses));
}
/**
* Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
* the DNS server addresses in a sequential order.
* Returns the {@link DnsServerAddresses} that yields the specified {@code addresses} sequentially. Once the
* last address is yielded, it will start again from the first address.
*/
public static Iterable<InetSocketAddress> sequential(InetSocketAddress... addresses) {
public static DnsServerAddresses sequential(InetSocketAddress... addresses) {
return sequential0(sanitize(addresses));
}
private static Iterable<InetSocketAddress> sequential0(final InetSocketAddress[] addresses) {
return new Iterable<InetSocketAddress>() {
@Override
public Iterator<InetSocketAddress> iterator() {
return new SequentialAddressIterator(addresses, 0);
}
};
}
/**
* Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
* the DNS server addresses in a shuffled order.
*/
public static Iterable<InetSocketAddress> shuffled(Iterable<? extends InetSocketAddress> addresses) {
return shuffled0(sanitize(addresses));
}
/**
* Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
* the DNS server addresses in a shuffled order.
*/
public static Iterable<InetSocketAddress> shuffled(InetSocketAddress... addresses) {
return shuffled0(sanitize(addresses));
}
private static Iterable<InetSocketAddress> shuffled0(final InetSocketAddress[] addresses) {
private static DnsServerAddresses sequential0(final InetSocketAddress... addresses) {
if (addresses.length == 1) {
return singleton(addresses[0]);
}
return new Iterable<InetSocketAddress>() {
return new DefaultDnsServerAddresses("sequential", addresses) {
@Override
public Iterator<InetSocketAddress> iterator() {
return new ShuffledAddressIterator(addresses);
public DnsServerAddressStream stream() {
return new SequentialDnsServerAddressStream(addresses, 0);
}
};
}
/**
* Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
* the DNS server addresses in a rotational order. It is similar to {@link #sequential(Iterable)}, but each
* {@link Iterator} starts from a different starting point. For example, the first {@link Iterable#iterator()}
* will iterate from the first DNS server address, the second one will iterate from the second DNS server address,
* and so on.
* Returns the {@link DnsServerAddresses} that yields the specified {@code address} in a shuffled order. Once all
* addresses are yielded, the addresses are shuffled again.
*/
public static Iterable<InetSocketAddress> rotational(Iterable<? extends InetSocketAddress> addresses) {
public static DnsServerAddresses shuffled(Iterable<? extends InetSocketAddress> addresses) {
return shuffled0(sanitize(addresses));
}
/**
* Returns the {@link DnsServerAddresses} that yields the specified {@code addresses} in a shuffled order. Once all
* addresses are yielded, the addresses are shuffled again.
*/
public static DnsServerAddresses shuffled(InetSocketAddress... addresses) {
return shuffled0(sanitize(addresses));
}
private static DnsServerAddresses shuffled0(final InetSocketAddress[] addresses) {
if (addresses.length == 1) {
return singleton(addresses[0]);
}
return new DefaultDnsServerAddresses("shuffled", addresses) {
@Override
public DnsServerAddressStream stream() {
return new ShuffledDnsServerAddressStream(addresses);
}
};
}
/**
* Returns the {@link DnsServerAddresses} that yields the specified {@code addresses} in a rotational sequential
* order. It is similar to {@link #sequential(Iterable)}, but each {@link DnsServerAddressStream} starts from
* a different starting point. For example, the first {@link #stream()} will start from the first address, the
* second one will start from the second address, and so on.
*/
public static DnsServerAddresses rotational(Iterable<? extends InetSocketAddress> addresses) {
return rotational0(sanitize(addresses));
}
/**
* Returns an infinite {@link Iterable} of the specified DNS server addresses, whose {@link Iterator} iterates
* the DNS server addresses in a rotational order. It is similar to {@link #sequential(Iterable)}, but each
* {@link Iterator} starts from a different starting point. For example, the first {@link Iterable#iterator()}
* will iterate from the first DNS server address, the second one will iterate from the second DNS server address,
* and so on.
* Returns the {@link DnsServerAddresses} that yields the specified {@code addresses} in a rotational sequential
* order. It is similar to {@link #sequential(Iterable)}, but each {@link DnsServerAddressStream} starts from
* a different starting point. For example, the first {@link #stream()} will start from the first address, the
* second one will start from the second address, and so on.
*/
public static Iterable<InetSocketAddress> rotational(InetSocketAddress... addresses) {
public static DnsServerAddresses rotational(InetSocketAddress... addresses) {
return rotational0(sanitize(addresses));
}
private static Iterable<InetSocketAddress> rotational0(final InetSocketAddress[] addresses) {
return new RotationalAddresses(addresses);
private static DnsServerAddresses rotational0(final InetSocketAddress[] addresses) {
if (addresses.length == 1) {
return singleton(addresses[0]);
}
return new RotationalDnsServerAddresses(addresses);
}
/**
* Returns an infinite {@link Iterable} of the specified DNS server address, whose {@link Iterator} always
* return the same DNS server address.
* Returns the {@link DnsServerAddresses} that yields only a single {@code address}.
*/
public static Iterable<InetSocketAddress> singleton(final InetSocketAddress address) {
public static DnsServerAddresses singleton(final InetSocketAddress address) {
if (address == null) {
throw new NullPointerException("address");
}
@ -195,30 +204,7 @@ public final class DnsServerAddresses {
throw new IllegalArgumentException("cannot use an unresolved DNS server address: " + address);
}
return new Iterable<InetSocketAddress>() {
private final Iterator<InetSocketAddress> iterator = new Iterator<InetSocketAddress>() {
@Override
public boolean hasNext() {
return true;
}
@Override
public InetSocketAddress next() {
return address;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
@Override
public Iterator<InetSocketAddress> iterator() {
return iterator;
}
};
return new SingletonDnsServerAddresses(address);
}
private static InetSocketAddress[] sanitize(Iterable<? extends InetSocketAddress> addresses) {
@ -244,7 +230,7 @@ public final class DnsServerAddresses {
}
if (list.isEmpty()) {
return DEFAULT_NAME_SERVER_ARRAY;
throw new IllegalArgumentException("empty addresses");
}
return list.toArray(new InetSocketAddress[list.size()]);
@ -273,123 +259,9 @@ public final class DnsServerAddresses {
return list.toArray(new InetSocketAddress[list.size()]);
}
private DnsServerAddresses() { }
private static final class SequentialAddressIterator implements Iterator<InetSocketAddress> {
private final InetSocketAddress[] addresses;
private int i;
SequentialAddressIterator(InetSocketAddress[] addresses, int startIdx) {
this.addresses = addresses;
i = startIdx;
}
@Override
public boolean hasNext() {
return true;
}
@Override
public InetSocketAddress next() {
int i = this.i;
InetSocketAddress next = addresses[i];
if (++ i < addresses.length) {
this.i = i;
} else {
this.i = 0;
}
return next;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private static final class ShuffledAddressIterator implements Iterator<InetSocketAddress> {
private final InetSocketAddress[] addresses;
private int i;
ShuffledAddressIterator(InetSocketAddress[] addresses) {
this.addresses = addresses.clone();
shuffle();
}
private void shuffle() {
final InetSocketAddress[] addresses = this.addresses;
final Random r = ThreadLocalRandom.current();
for (int i = addresses.length - 1; i >= 0; i --) {
InetSocketAddress tmp = addresses[i];
int j = r.nextInt(i + 1);
addresses[i] = addresses[j];
addresses[j] = tmp;
}
}
@Override
public boolean hasNext() {
return true;
}
@Override
public InetSocketAddress next() {
int i = this.i;
InetSocketAddress next = addresses[i];
if (++ i < addresses.length) {
this.i = i;
} else {
this.i = 0;
shuffle();
}
return next;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private static final class RotationalAddresses implements Iterable<InetSocketAddress> {
private static final AtomicIntegerFieldUpdater<RotationalAddresses> startIdxUpdater;
static {
AtomicIntegerFieldUpdater<RotationalAddresses> updater =
PlatformDependent.newAtomicIntegerFieldUpdater(RotationalAddresses.class, "startIdx");
if (updater == null) {
updater = AtomicIntegerFieldUpdater.newUpdater(RotationalAddresses.class, "startIdx");
}
startIdxUpdater = updater;
}
private final InetSocketAddress[] addresses;
@SuppressWarnings("UnusedDeclaration")
private volatile int startIdx;
RotationalAddresses(InetSocketAddress[] addresses) {
this.addresses = addresses;
}
@Override
public Iterator<InetSocketAddress> iterator() {
for (;;) {
int curStartIdx = startIdx;
int nextStartIdx = curStartIdx + 1;
if (nextStartIdx >= addresses.length) {
nextStartIdx = 0;
}
if (startIdxUpdater.compareAndSet(this, curStartIdx, nextStartIdx)) {
return new SequentialAddressIterator(addresses, curStartIdx);
}
}
}
}
/**
* Starts a new infinite stream of DNS server addresses. This method is invoked by {@link DnsNameResolver} on every
* uncached {@link DnsNameResolver#resolve(SocketAddress)} or {@link DnsNameResolver#resolveAll(SocketAddress)}.
*/
public abstract DnsServerAddressStream stream();
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project 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 io.netty.resolver.dns;
import io.netty.util.internal.PlatformDependent;
import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
final class RotationalDnsServerAddresses extends DefaultDnsServerAddresses {
private static final AtomicIntegerFieldUpdater<RotationalDnsServerAddresses> startIdxUpdater;
static {
AtomicIntegerFieldUpdater<RotationalDnsServerAddresses> updater =
PlatformDependent.newAtomicIntegerFieldUpdater(RotationalDnsServerAddresses.class, "startIdx");
if (updater == null) {
updater = AtomicIntegerFieldUpdater.newUpdater(RotationalDnsServerAddresses.class, "startIdx");
}
startIdxUpdater = updater;
}
@SuppressWarnings("UnusedDeclaration")
private volatile int startIdx;
RotationalDnsServerAddresses(InetSocketAddress[] addresses) {
super("rotational", addresses);
}
@Override
public DnsServerAddressStream stream() {
for (;;) {
int curStartIdx = startIdx;
int nextStartIdx = curStartIdx + 1;
if (nextStartIdx >= addresses.length) {
nextStartIdx = 0;
}
if (startIdxUpdater.compareAndSet(this, curStartIdx, nextStartIdx)) {
return new SequentialDnsServerAddressStream(addresses, curStartIdx);
}
}
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project 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 io.netty.resolver.dns;
import java.net.InetSocketAddress;
final class SequentialDnsServerAddressStream implements DnsServerAddressStream {
private final InetSocketAddress[] addresses;
private int i;
SequentialDnsServerAddressStream(InetSocketAddress[] addresses, int startIdx) {
this.addresses = addresses;
i = startIdx;
}
@Override
public InetSocketAddress next() {
int i = this.i;
InetSocketAddress next = addresses[i];
if (++ i < addresses.length) {
this.i = i;
} else {
this.i = 0;
}
return next;
}
@Override
public String toString() {
return toString("sequential", i, addresses);
}
static String toString(String type, int index, InetSocketAddress[] addresses) {
final StringBuilder buf = new StringBuilder(type.length() + 2 + addresses.length * 16);
buf.append(type).append("(index: ").append(index);
buf.append(", addrs: (");
for (InetSocketAddress a: addresses) {
buf.append(a).append(", ");
}
buf.setLength(buf.length() - 2);
buf.append("))");
return buf.toString();
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project 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 io.netty.resolver.dns;
import io.netty.util.internal.ThreadLocalRandom;
import java.net.InetSocketAddress;
import java.util.Random;
final class ShuffledDnsServerAddressStream implements DnsServerAddressStream {
private final InetSocketAddress[] addresses;
private int i;
ShuffledDnsServerAddressStream(InetSocketAddress[] addresses) {
this.addresses = addresses.clone();
shuffle();
}
private void shuffle() {
final InetSocketAddress[] addresses = this.addresses;
final Random r = ThreadLocalRandom.current();
for (int i = addresses.length - 1; i >= 0; i --) {
InetSocketAddress tmp = addresses[i];
int j = r.nextInt(i + 1);
addresses[i] = addresses[j];
addresses[j] = tmp;
}
}
@Override
public InetSocketAddress next() {
int i = this.i;
InetSocketAddress next = addresses[i];
if (++ i < addresses.length) {
this.i = i;
} else {
this.i = 0;
shuffle();
}
return next;
}
@Override
public String toString() {
return SequentialDnsServerAddressStream.toString("shuffled", i, addresses);
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project 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 io.netty.resolver.dns;
import java.net.InetSocketAddress;
final class SingletonDnsServerAddresses extends DnsServerAddresses {
private final InetSocketAddress address;
private final String strVal;
private final DnsServerAddressStream stream = new DnsServerAddressStream() {
@Override
public InetSocketAddress next() {
return address;
}
@Override
public String toString() {
return SingletonDnsServerAddresses.this.toString();
}
};
SingletonDnsServerAddresses(InetSocketAddress address) {
this.address = address;
strVal = new StringBuilder(32).append("singleton(").append(address).append(')').toString();
}
@Override
public DnsServerAddressStream stream() {
return stream;
}
@Override
public String toString() {
return strVal;
}
}

View File

@ -22,7 +22,6 @@ import org.junit.Test;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Set;
import static org.hamcrest.Matchers.*;
@ -36,16 +35,16 @@ public class DnsServerAddressesTest {
@Test
public void testDefaultAddresses() {
assertThat(DnsServerAddresses.defaultAddresses().size(), is(greaterThan(0)));
assertThat(DnsServerAddresses.defaultAddressList().size(), is(greaterThan(0)));
}
@Test
public void testSequential() {
Iterable<InetSocketAddress> seq = DnsServerAddresses.sequential(ADDR1, ADDR2, ADDR3);
assertThat(seq.iterator(), is(not(sameInstance(seq.iterator()))));
DnsServerAddresses seq = DnsServerAddresses.sequential(ADDR1, ADDR2, ADDR3);
assertThat(seq.stream(), is(not(sameInstance(seq.stream()))));
for (int j = 0; j < 2; j ++) {
Iterator<InetSocketAddress> i = seq.iterator();
DnsServerAddressStream i = seq.stream();
assertNext(i, ADDR1);
assertNext(i, ADDR2);
assertNext(i, ADDR3);
@ -57,9 +56,9 @@ public class DnsServerAddressesTest {
@Test
public void testRotational() {
Iterable<InetSocketAddress> seq = DnsServerAddresses.rotational(ADDR1, ADDR2, ADDR3);
DnsServerAddresses seq = DnsServerAddresses.rotational(ADDR1, ADDR2, ADDR3);
Iterator<InetSocketAddress> i = seq.iterator();
DnsServerAddressStream i = seq.stream();
assertNext(i, ADDR1);
assertNext(i, ADDR2);
assertNext(i, ADDR3);
@ -67,7 +66,7 @@ public class DnsServerAddressesTest {
assertNext(i, ADDR2);
assertNext(i, ADDR3);
i = seq.iterator();
i = seq.stream();
assertNext(i, ADDR2);
assertNext(i, ADDR3);
assertNext(i, ADDR1);
@ -75,7 +74,7 @@ public class DnsServerAddressesTest {
assertNext(i, ADDR3);
assertNext(i, ADDR1);
i = seq.iterator();
i = seq.stream();
assertNext(i, ADDR3);
assertNext(i, ADDR1);
assertNext(i, ADDR2);
@ -83,7 +82,7 @@ public class DnsServerAddressesTest {
assertNext(i, ADDR1);
assertNext(i, ADDR2);
i = seq.iterator();
i = seq.stream();
assertNext(i, ADDR1);
assertNext(i, ADDR2);
assertNext(i, ADDR3);
@ -94,36 +93,34 @@ public class DnsServerAddressesTest {
@Test
public void testShuffled() {
Iterable<InetSocketAddress> seq = DnsServerAddresses.shuffled(ADDR1, ADDR2, ADDR3);
DnsServerAddresses seq = DnsServerAddresses.shuffled(ADDR1, ADDR2, ADDR3);
// Ensure that all three addresses are returned by the iterator.
// In theory, this test can fail at extremely low chance, but we don't really care.
Set<InetSocketAddress> set = Collections.newSetFromMap(new IdentityHashMap<InetSocketAddress, Boolean>());
Iterator<InetSocketAddress> i = seq.iterator();
DnsServerAddressStream i = seq.stream();
for (int j = 0; j < 1048576; j ++) {
assertThat(i.hasNext(), is(true));
set.add(i.next());
}
assertThat(set.size(), is(3));
assertThat(seq.iterator(), is(not(sameInstance(seq.iterator()))));
assertThat(seq.stream(), is(not(sameInstance(seq.stream()))));
}
@Test
public void testSingleton() {
Iterable<InetSocketAddress> seq = DnsServerAddresses.singleton(ADDR1);
DnsServerAddresses seq = DnsServerAddresses.singleton(ADDR1);
// Should return the same iterator instance for least possible footprint.
assertThat(seq.iterator(), is(sameInstance(seq.iterator())));
assertThat(seq.stream(), is(sameInstance(seq.stream())));
Iterator<InetSocketAddress> i = seq.iterator();
DnsServerAddressStream i = seq.stream();
assertNext(i, ADDR1);
assertNext(i, ADDR1);
assertNext(i, ADDR1);
}
private static void assertNext(Iterator<InetSocketAddress> i, InetSocketAddress addr) {
assertThat(i.hasNext(), is(true));
private static void assertNext(DnsServerAddressStream i, InetSocketAddress addr) {
assertThat(i.next(), is(sameInstance(addr)));
}
}