Extract SocketAdress logic from NameResolver

Motivation:

As discussed in #4529, NameResolver design shouldn't be resolving SocketAddresses (or String name + port) and return InetSocketAddresses. It should resolve String names and return InetAddresses.
This SocketAddress to InetSocketAddresses resolution is actually a different concern, used by Bootstrap.

Modifications:

Extract SocketAddress to InetSocketAddresses resolution concern to a new class hierarchy named AddressResolver.
These AddressResolvers delegate to NameResolvers.

Result:

Better separation of concerns.

Note that new AddressResolvers generate a bit more allocations because of the intermediate Promise and List<InetAddress>.
This commit is contained in:
Stephane Landelle 2015-12-13 00:11:59 +01:00 committed by Norman Maurer
parent 3e7bd25589
commit 541fe86fe0
18 changed files with 603 additions and 401 deletions

View File

@ -36,7 +36,7 @@ import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.resolver.NoopNameResolverGroup; import io.netty.resolver.NoopAddressResolverGroup;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.DefaultExecutorServiceFactory; import io.netty.util.concurrent.DefaultExecutorServiceFactory;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
@ -523,7 +523,7 @@ public class ProxyHandlerTest {
Bootstrap b = new Bootstrap(); Bootstrap b = new Bootstrap();
b.group(group); b.group(group);
b.channel(NioSocketChannel.class); b.channel(NioSocketChannel.class);
b.resolver(NoopNameResolverGroup.INSTANCE); b.resolver(NoopAddressResolverGroup.INSTANCE);
b.handler(new ChannelInitializer<SocketChannel>() { b.handler(new ChannelInitializer<SocketChannel>() {
@Override @Override
protected void initChannel(SocketChannel ch) throws Exception { protected void initChannel(SocketChannel ch) throws Exception {
@ -571,7 +571,7 @@ public class ProxyHandlerTest {
Bootstrap b = new Bootstrap(); Bootstrap b = new Bootstrap();
b.group(group); b.group(group);
b.channel(NioSocketChannel.class); b.channel(NioSocketChannel.class);
b.resolver(NoopNameResolverGroup.INSTANCE); b.resolver(NoopAddressResolverGroup.INSTANCE);
b.handler(new ChannelInitializer<SocketChannel>() { b.handler(new ChannelInitializer<SocketChannel>() {
@Override @Override
protected void initChannel(SocketChannel ch) throws Exception { protected void initChannel(SocketChannel ch) throws Exception {
@ -616,7 +616,7 @@ public class ProxyHandlerTest {
Bootstrap b = new Bootstrap(); Bootstrap b = new Bootstrap();
b.group(group); b.group(group);
b.channel(NioSocketChannel.class); b.channel(NioSocketChannel.class);
b.resolver(NoopNameResolverGroup.INSTANCE); b.resolver(NoopAddressResolverGroup.INSTANCE);
b.handler(new ChannelInitializer<SocketChannel>() { b.handler(new ChannelInitializer<SocketChannel>() {
@Override @Override
protected void initChannel(SocketChannel ch) throws Exception { protected void initChannel(SocketChannel ch) throws Exception {

View File

@ -20,8 +20,8 @@ import io.netty.channel.ChannelFactory;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import io.netty.channel.ReflectiveChannelFactory; import io.netty.channel.ReflectiveChannelFactory;
import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramChannel;
import io.netty.resolver.NameResolver; import io.netty.resolver.AddressResolver;
import io.netty.resolver.NameResolverGroup; import io.netty.resolver.AddressResolverGroup;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
@ -30,31 +30,31 @@ import java.net.InetSocketAddress;
import static io.netty.resolver.dns.DnsNameResolver.ANY_LOCAL_ADDR; import static io.netty.resolver.dns.DnsNameResolver.ANY_LOCAL_ADDR;
/** /**
* A {@link NameResolverGroup} of {@link DnsNameResolver}s. * A {@link AddressResolverGroup} of {@link DnsNameResolver}s.
*/ */
public class DnsNameResolverGroup extends NameResolverGroup<InetSocketAddress> { public class DnsAddressResolverGroup extends AddressResolverGroup<InetSocketAddress> {
private final ChannelFactory<? extends DatagramChannel> channelFactory; private final ChannelFactory<? extends DatagramChannel> channelFactory;
private final InetSocketAddress localAddress; private final InetSocketAddress localAddress;
private final DnsServerAddresses nameServerAddresses; private final DnsServerAddresses nameServerAddresses;
public DnsNameResolverGroup( public DnsAddressResolverGroup(
Class<? extends DatagramChannel> channelType, DnsServerAddresses nameServerAddresses) { Class<? extends DatagramChannel> channelType, DnsServerAddresses nameServerAddresses) {
this(channelType, ANY_LOCAL_ADDR, nameServerAddresses); this(channelType, ANY_LOCAL_ADDR, nameServerAddresses);
} }
public DnsNameResolverGroup( public DnsAddressResolverGroup(
Class<? extends DatagramChannel> channelType, Class<? extends DatagramChannel> channelType,
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) { InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) {
this(new ReflectiveChannelFactory<DatagramChannel>(channelType), localAddress, nameServerAddresses); this(new ReflectiveChannelFactory<DatagramChannel>(channelType), localAddress, nameServerAddresses);
} }
public DnsNameResolverGroup( public DnsAddressResolverGroup(
ChannelFactory<? extends DatagramChannel> channelFactory, DnsServerAddresses nameServerAddresses) { ChannelFactory<? extends DatagramChannel> channelFactory, DnsServerAddresses nameServerAddresses) {
this(channelFactory, ANY_LOCAL_ADDR, nameServerAddresses); this(channelFactory, ANY_LOCAL_ADDR, nameServerAddresses);
} }
public DnsNameResolverGroup( public DnsAddressResolverGroup(
ChannelFactory<? extends DatagramChannel> channelFactory, ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) { InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) {
this.channelFactory = channelFactory; this.channelFactory = channelFactory;
@ -63,7 +63,7 @@ public class DnsNameResolverGroup extends NameResolverGroup<InetSocketAddress> {
} }
@Override @Override
protected final NameResolver<InetSocketAddress> newResolver(EventExecutor executor) throws Exception { protected final AddressResolver<InetSocketAddress> newResolver(EventExecutor executor) throws Exception {
if (!(executor instanceof EventLoop)) { if (!(executor instanceof EventLoop)) {
throw new IllegalStateException( throw new IllegalStateException(
"unsupported executor type: " + StringUtil.simpleClassName(executor) + "unsupported executor type: " + StringUtil.simpleClassName(executor) +
@ -77,11 +77,11 @@ public class DnsNameResolverGroup extends NameResolverGroup<InetSocketAddress> {
* Creates a new {@link DnsNameResolver}. Override this method to create an alternative {@link DnsNameResolver} * Creates a new {@link DnsNameResolver}. Override this method to create an alternative {@link DnsNameResolver}
* implementation or override the default configuration. * implementation or override the default configuration.
*/ */
protected DnsNameResolver newResolver( protected AddressResolver<InetSocketAddress> newResolver(
EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory, EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) throws Exception { InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) throws Exception {
return new DnsNameResolver( return new DnsNameResolver(eventLoop, channelFactory, localAddress, nameServerAddresses)
eventLoop, channelFactory, localAddress, nameServerAddresses); .asAddressResolver();
} }
} }

View File

@ -33,8 +33,7 @@ import io.netty.handler.codec.dns.DatagramDnsResponse;
import io.netty.handler.codec.dns.DatagramDnsResponseDecoder; import io.netty.handler.codec.dns.DatagramDnsResponseDecoder;
import io.netty.handler.codec.dns.DnsQuestion; import io.netty.handler.codec.dns.DnsQuestion;
import io.netty.handler.codec.dns.DnsResponse; import io.netty.handler.codec.dns.DnsResponse;
import io.netty.resolver.NameResolver; import io.netty.resolver.InetNameResolver;
import io.netty.resolver.SimpleNameResolver;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.FastThreadLocal; import io.netty.util.concurrent.FastThreadLocal;
@ -42,14 +41,12 @@ import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import io.netty.util.internal.OneTimeTask; import io.netty.util.internal.OneTimeTask;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import java.net.IDN; import java.net.IDN;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -62,9 +59,9 @@ import java.util.concurrent.TimeUnit;
import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.util.internal.ObjectUtil.checkNotNull;
/** /**
* A DNS-based {@link NameResolver}. * A DNS-based {@link InetNameResolver}.
*/ */
public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> { public class DnsNameResolver extends InetNameResolver {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsNameResolver.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsNameResolver.class);
@ -74,7 +71,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
static { static {
// Note that we did not use SystemPropertyUtil.getBoolean() here to emulate the behavior of JDK. // Note that we did not use SystemPropertyUtil.getBoolean() here to emulate the behavior of JDK.
if ("true".equalsIgnoreCase(SystemPropertyUtil.get("java.net.preferIPv6Addresses"))) { if (Boolean.getBoolean("java.net.preferIPv6Addresses")) {
DEFAULT_RESOLVE_ADDRESS_TYPES[0] = InternetProtocolFamily.IPv6; DEFAULT_RESOLVE_ADDRESS_TYPES[0] = InternetProtocolFamily.IPv6;
DEFAULT_RESOLVE_ADDRESS_TYPES[1] = InternetProtocolFamily.IPv4; DEFAULT_RESOLVE_ADDRESS_TYPES[1] = InternetProtocolFamily.IPv4;
logger.debug("-Djava.net.preferIPv6Addresses: true"); logger.debug("-Djava.net.preferIPv6Addresses: true");
@ -98,7 +95,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
final DnsQueryContextManager queryContextManager = new DnsQueryContextManager(); final DnsQueryContextManager queryContextManager = new DnsQueryContextManager();
/** /**
* Cache for {@link #doResolve(InetSocketAddress, Promise)} and {@link #doResolveAll(InetSocketAddress, Promise)}. * Cache for {@link #doResolve(String, Promise)} and {@link #doResolveAll(String, Promise)}.
*/ */
final ConcurrentMap<String, List<DnsCacheEntry>> resolveCache = PlatformDependent.newConcurrentHashMap(); final ConcurrentMap<String, List<DnsCacheEntry>> resolveCache = PlatformDependent.newConcurrentHashMap();
@ -329,7 +326,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
} }
/** /**
* Returns the list of the protocol families of the address resolved by {@link #resolve(SocketAddress)} * Returns the list of the protocol families of the address resolved by {@link #resolve(String)}
* in the order of preference. * in the order of preference.
* The default value depends on the value of the system property {@code "java.net.preferIPv6Addresses"}. * The default value depends on the value of the system property {@code "java.net.preferIPv6Addresses"}.
* *
@ -344,7 +341,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
} }
/** /**
* Sets the list of the protocol families of the address resolved by {@link #resolve(SocketAddress)}. * Sets the list of the protocol families of the address resolved by {@link #resolve(String)}.
* Usually, both {@link InternetProtocolFamily#IPv4} and {@link InternetProtocolFamily#IPv6} are specified in the * Usually, both {@link InternetProtocolFamily#IPv4} and {@link InternetProtocolFamily#IPv6} are specified in the
* order of preference. To enforce the resolve to retrieve the address of a specific protocol family, specify * order of preference. To enforce the resolve to retrieve the address of a specific protocol family, specify
* only a single {@link InternetProtocolFamily}. * only a single {@link InternetProtocolFamily}.
@ -382,7 +379,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
} }
/** /**
* Sets the list of the protocol families of the address resolved by {@link #resolve(SocketAddress)}. * Sets the list of the protocol families of the address resolved by {@link #resolve(String)}.
* Usually, both {@link InternetProtocolFamily#IPv4} and {@link InternetProtocolFamily#IPv6} are specified in the * Usually, both {@link InternetProtocolFamily#IPv4} and {@link InternetProtocolFamily#IPv6} are specified in the
* order of preference. To enforce the resolve to retrieve the address of a specific protocol family, specify * order of preference. To enforce the resolve to retrieve the address of a specific protocol family, specify
* only a single {@link InternetProtocolFamily}. * only a single {@link InternetProtocolFamily}.
@ -594,28 +591,22 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
} }
@Override @Override
protected boolean doIsResolved(InetSocketAddress address) { protected void doResolve(String inetHost, Promise<InetAddress> promise) throws Exception {
return !address.isUnresolved(); final byte[] bytes = NetUtil.createByteArrayFromIpAddressString(inetHost);
}
@Override
protected void doResolve(InetSocketAddress unresolvedAddress, Promise<InetSocketAddress> promise) throws Exception {
final byte[] bytes = NetUtil.createByteArrayFromIpAddressString(unresolvedAddress.getHostName());
if (bytes != null) { if (bytes != null) {
// The unresolvedAddress was created via a String that contains an ipaddress. // The inetHost is actually an ipaddress.
promise.setSuccess(new InetSocketAddress(InetAddress.getByAddress(bytes), unresolvedAddress.getPort())); promise.setSuccess(InetAddress.getByAddress(bytes));
return; return;
} }
final String hostname = hostname(unresolvedAddress); final String hostname = hostname(inetHost);
final int port = unresolvedAddress.getPort();
if (!doResolveCached(hostname, port, promise)) { if (!doResolveCached(hostname, promise)) {
doResolveUncached(hostname, port, promise); doResolveUncached(hostname, promise);
} }
} }
private boolean doResolveCached(String hostname, int port, Promise<InetSocketAddress> promise) { private boolean doResolveCached(String hostname, Promise<InetAddress> promise) {
final List<DnsCacheEntry> cachedEntries = resolveCache.get(hostname); final List<DnsCacheEntry> cachedEntries = resolveCache.get(hostname);
if (cachedEntries == null) { if (cachedEntries == null) {
return false; return false;
@ -644,7 +635,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
} }
if (address != null) { if (address != null) {
setSuccess(promise, new InetSocketAddress(address, port)); setSuccess(promise, address);
} else if (cause != null) { } else if (cause != null) {
if (!promise.tryFailure(cause)) { if (!promise.tryFailure(cause)) {
logger.warn("Failed to notify failure to a promise: {}", promise, cause); logger.warn("Failed to notify failure to a promise: {}", promise, cause);
@ -656,15 +647,15 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
return true; return true;
} }
private static void setSuccess(Promise<InetSocketAddress> promise, InetSocketAddress result) { private static void setSuccess(Promise<InetAddress> promise, InetAddress result) {
if (!promise.trySuccess(result)) { if (!promise.trySuccess(result)) {
logger.warn("Failed to notify success ({}) to a promise: {}", result, promise); logger.warn("Failed to notify success ({}) to a promise: {}", result, promise);
} }
} }
private void doResolveUncached(String hostname, final int port, Promise<InetSocketAddress> promise) { private void doResolveUncached(String hostname, Promise<InetAddress> promise) {
final DnsNameResolverContext<InetSocketAddress> ctx = final DnsNameResolverContext<InetAddress> ctx =
new DnsNameResolverContext<InetSocketAddress>(this, hostname, promise) { new DnsNameResolverContext<InetAddress>(this, hostname, promise) {
@Override @Override
protected boolean finishResolve( protected boolean finishResolve(
Class<? extends InetAddress> addressType, List<DnsCacheEntry> resolvedEntries) { Class<? extends InetAddress> addressType, List<DnsCacheEntry> resolvedEntries) {
@ -673,7 +664,7 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
for (int i = 0; i < numEntries; i++) { for (int i = 0; i < numEntries; i++) {
final InetAddress a = resolvedEntries.get(i).address(); final InetAddress a = resolvedEntries.get(i).address();
if (addressType.isInstance(a)) { if (addressType.isInstance(a)) {
setSuccess(promise(), new InetSocketAddress(a, port)); setSuccess(promise(), a);
return true; return true;
} }
} }
@ -685,32 +676,29 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
} }
@Override @Override
protected void doResolveAll( protected void doResolveAll(String inetHost, Promise<List<InetAddress>> promise) throws Exception {
InetSocketAddress unresolvedAddress, Promise<List<InetSocketAddress>> promise) throws Exception {
final byte[] bytes = NetUtil.createByteArrayFromIpAddressString(unresolvedAddress.getHostName()); final byte[] bytes = NetUtil.createByteArrayFromIpAddressString(inetHost);
if (bytes != null) { if (bytes != null) {
// The unresolvedAddress was created via a String that contains an ipaddress. // The unresolvedAddress was created via a String that contains an ipaddress.
promise.setSuccess(Collections.singletonList( promise.setSuccess(Collections.singletonList(InetAddress.getByAddress(bytes)));
new InetSocketAddress(InetAddress.getByAddress(bytes), unresolvedAddress.getPort())));
return; return;
} }
final String hostname = hostname(unresolvedAddress); final String hostname = hostname(inetHost);
final int port = unresolvedAddress.getPort();
if (!doResolveAllCached(hostname, port, promise)) { if (!doResolveAllCached(hostname, promise)) {
doResolveAllUncached(hostname, port, promise); doResolveAllUncached(hostname, promise);
} }
} }
private boolean doResolveAllCached(String hostname, int port, Promise<List<InetSocketAddress>> promise) { private boolean doResolveAllCached(String hostname, Promise<List<InetAddress>> promise) {
final List<DnsCacheEntry> cachedEntries = resolveCache.get(hostname); final List<DnsCacheEntry> cachedEntries = resolveCache.get(hostname);
if (cachedEntries == null) { if (cachedEntries == null) {
return false; return false;
} }
List<InetSocketAddress> result = null; List<InetAddress> result = null;
Throwable cause = null; Throwable cause = null;
synchronized (cachedEntries) { synchronized (cachedEntries) {
final int numEntries = cachedEntries.size(); final int numEntries = cachedEntries.size();
@ -724,9 +712,9 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
final DnsCacheEntry e = cachedEntries.get(i); final DnsCacheEntry e = cachedEntries.get(i);
if (f.addressType().isInstance(e.address())) { if (f.addressType().isInstance(e.address())) {
if (result == null) { if (result == null) {
result = new ArrayList<InetSocketAddress>(numEntries); result = new ArrayList<InetAddress>(numEntries);
} }
result.add(new InetSocketAddress(e.address(), port)); result.add(e.address());
} }
} }
} }
@ -744,23 +732,22 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
return true; return true;
} }
private void doResolveAllUncached(final String hostname, final int port, private void doResolveAllUncached(final String hostname, final Promise<List<InetAddress>> promise) {
final Promise<List<InetSocketAddress>> promise) { final DnsNameResolverContext<List<InetAddress>> ctx =
final DnsNameResolverContext<List<InetSocketAddress>> ctx = new DnsNameResolverContext<List<InetAddress>>(this, hostname, promise) {
new DnsNameResolverContext<List<InetSocketAddress>>(this, hostname, promise) {
@Override @Override
protected boolean finishResolve( protected boolean finishResolve(
Class<? extends InetAddress> addressType, List<DnsCacheEntry> resolvedEntries) { Class<? extends InetAddress> addressType, List<DnsCacheEntry> resolvedEntries) {
List<InetSocketAddress> result = null; List<InetAddress> result = null;
final int numEntries = resolvedEntries.size(); final int numEntries = resolvedEntries.size();
for (int i = 0; i < numEntries; i++) { for (int i = 0; i < numEntries; i++) {
final InetAddress a = resolvedEntries.get(i).address(); final InetAddress a = resolvedEntries.get(i).address();
if (addressType.isInstance(a)) { if (addressType.isInstance(a)) {
if (result == null) { if (result == null) {
result = new ArrayList<InetSocketAddress>(numEntries); result = new ArrayList<InetAddress>(numEntries);
} }
result.add(new InetSocketAddress(a, port)); result.add(a);
} }
} }
@ -775,16 +762,8 @@ public class DnsNameResolver extends SimpleNameResolver<InetSocketAddress> {
ctx.resolve(); ctx.resolve();
} }
private static String hostname(InetSocketAddress addr) { private static String hostname(String inetHost) {
// InetSocketAddress.getHostString() is available since Java 7. return IDN.toASCII(inetHost);
final String hostname;
if (PlatformDependent.javaVersion() < 7) {
hostname = addr.getHostName();
} else {
hostname = addr.getHostString();
}
return IDN.toASCII(hostname);
} }
void cache(String hostname, InetAddress address, long originalTtl) { void cache(String hostname, InetAddress address, long originalTtl) {

View File

@ -320,7 +320,7 @@ public class DnsNameResolverTest {
InetAddress actual = resultB.get(e.getKey()); InetAddress actual = resultB.get(e.getKey());
if (!actual.equals(expected)) { if (!actual.equals(expected)) {
// Print the content of the cache when test failure is expected. // Print the content of the cache when test failure is expected.
System.err.println("Cache for " + e.getKey() + ": " + resolver.resolveAll(e.getKey(), 0).getNow()); System.err.println("Cache for " + e.getKey() + ": " + resolver.resolveAll(e.getKey()).getNow());
} }
assertThat(actual, is(expected)); assertThat(actual, is(expected));
} }
@ -347,8 +347,8 @@ public class DnsNameResolverTest {
final Map<String, InetAddress> results = new HashMap<String, InetAddress>(); final Map<String, InetAddress> results = new HashMap<String, InetAddress>();
try { try {
final Map<InetSocketAddress, Future<InetSocketAddress>> futures = final Map<String, Future<InetAddress>> futures =
new LinkedHashMap<InetSocketAddress, Future<InetSocketAddress>>(); new LinkedHashMap<String, Future<InetAddress>>();
for (String name : DOMAINS) { for (String name : DOMAINS) {
if (excludedDomains.contains(name)) { if (excludedDomains.contains(name)) {
@ -358,19 +358,17 @@ public class DnsNameResolverTest {
resolve(futures, name); resolve(futures, name);
} }
for (Entry<InetSocketAddress, Future<InetSocketAddress>> e : futures.entrySet()) { for (Entry<String, Future<InetAddress>> e : futures.entrySet()) {
InetSocketAddress unresolved = e.getKey(); String unresolved = e.getKey();
InetSocketAddress resolved = e.getValue().sync().getNow(); InetAddress resolved = e.getValue().sync().getNow();
logger.info("{}: {}", unresolved.getHostString(), resolved.getAddress().getHostAddress()); logger.info("{}: {}", unresolved, resolved.getHostAddress());
assertThat(resolved.isUnresolved(), is(false)); assertThat(resolved.getHostName(), is(unresolved));
assertThat(resolved.getHostString(), is(unresolved.getHostString()));
assertThat(resolved.getPort(), is(unresolved.getPort()));
boolean typeMatches = false; boolean typeMatches = false;
for (InternetProtocolFamily f: famililies) { for (InternetProtocolFamily f: famililies) {
Class<?> resolvedType = resolved.getAddress().getClass(); Class<?> resolvedType = resolved.getClass();
if (f.addressType().isAssignableFrom(resolvedType)) { if (f.addressType().isAssignableFrom(resolvedType)) {
typeMatches = true; typeMatches = true;
} }
@ -378,7 +376,7 @@ public class DnsNameResolverTest {
assertThat(typeMatches, is(true)); assertThat(typeMatches, is(true));
results.put(resolved.getHostString(), resolved.getAddress()); results.put(resolved.getHostName(), resolved);
} }
} finally { } finally {
resolver.setResolveAddressTypes(oldResolveAddressTypes); resolver.setResolveAddressTypes(oldResolveAddressTypes);
@ -477,7 +475,7 @@ public class DnsNameResolverTest {
private static UnknownHostException resolveNonExistentDomain() { private static UnknownHostException resolveNonExistentDomain() {
try { try {
resolver.resolve("non-existent.netty.io", 0).sync(); resolver.resolve("non-existent.netty.io").sync();
fail(); fail();
return null; return null;
} catch (Exception e) { } catch (Exception e) {
@ -488,20 +486,14 @@ public class DnsNameResolverTest {
@Test @Test
public void testResolveIp() { public void testResolveIp() {
InetSocketAddress unresolved = InetAddress address = resolver.resolve("10.0.0.1").syncUninterruptibly().getNow();
InetSocketAddress.createUnresolved("10.0.0.1", ThreadLocalRandom.current().nextInt(65536));
InetSocketAddress address = resolver.resolve(unresolved).syncUninterruptibly().getNow();
assertEquals("10.0.0.1", address.getHostName()); assertEquals("10.0.0.1", address.getHostName());
} }
private static void resolve( private static void resolve(Map<String, Future<InetAddress>> futures, String hostname) {
Map<InetSocketAddress, Future<InetSocketAddress>> futures, String hostname) {
InetSocketAddress unresolved =
InetSocketAddress.createUnresolved(hostname, ThreadLocalRandom.current().nextInt(65536));
futures.put(unresolved, resolver.resolve(unresolved)); futures.put(hostname, resolver.resolve(hostname));
} }
private static void queryMx( private static void queryMx(

View File

@ -0,0 +1,206 @@
/*
* 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;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.TypeParameterMatcher;
import java.net.SocketAddress;
import java.nio.channels.UnsupportedAddressTypeException;
import java.util.Collections;
import java.util.List;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* A skeletal {@link AddressResolver} implementation.
*/
public abstract class AbstractAddressResolver<T extends SocketAddress> implements AddressResolver<T> {
private final EventExecutor executor;
private final TypeParameterMatcher matcher;
/**
* @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned
* by {@link #resolve(SocketAddress)}
*/
protected AbstractAddressResolver(EventExecutor executor) {
this.executor = checkNotNull(executor, "executor");
matcher = TypeParameterMatcher.find(this, AbstractAddressResolver.class, "T");
}
/**
* @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned
* by {@link #resolve(SocketAddress)}
* @param addressType the type of the {@link SocketAddress} supported by this resolver
*/
protected AbstractAddressResolver(EventExecutor executor, Class<? extends T> addressType) {
this.executor = checkNotNull(executor, "executor");
matcher = TypeParameterMatcher.get(addressType);
}
/**
* Returns the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned
* by {@link #resolve(SocketAddress)}.
*/
protected EventExecutor executor() {
return executor;
}
@Override
public boolean isSupported(SocketAddress address) {
return matcher.match(address);
}
@Override
public final boolean isResolved(SocketAddress address) {
if (!isSupported(address)) {
throw new UnsupportedAddressTypeException();
}
@SuppressWarnings("unchecked")
final T castAddress = (T) address;
return doIsResolved(castAddress);
}
/**
* Invoked by {@link #isResolved(SocketAddress)} to check if the specified {@code address} has been resolved
* already.
*/
protected abstract boolean doIsResolved(T address);
@Override
public final Future<T> resolve(SocketAddress address) {
if (!isSupported(checkNotNull(address, "address"))) {
// Address type not supported by the resolver
return executor().newFailedFuture(new UnsupportedAddressTypeException());
}
if (isResolved(address)) {
// Resolved already; no need to perform a lookup
@SuppressWarnings("unchecked")
final T cast = (T) address;
return executor.newSucceededFuture(cast);
}
try {
@SuppressWarnings("unchecked")
final T cast = (T) address;
final Promise<T> promise = executor().newPromise();
doResolve(cast, promise);
return promise;
} catch (Exception e) {
return executor().newFailedFuture(e);
}
}
@Override
public final Future<T> resolve(SocketAddress address, Promise<T> promise) {
checkNotNull(address, "address");
checkNotNull(promise, "promise");
if (!isSupported(address)) {
// Address type not supported by the resolver
return promise.setFailure(new UnsupportedAddressTypeException());
}
if (isResolved(address)) {
// Resolved already; no need to perform a lookup
@SuppressWarnings("unchecked")
final T cast = (T) address;
return promise.setSuccess(cast);
}
try {
@SuppressWarnings("unchecked")
final T cast = (T) address;
doResolve(cast, promise);
return promise;
} catch (Exception e) {
return promise.setFailure(e);
}
}
@Override
public final Future<List<T>> resolveAll(SocketAddress address) {
if (!isSupported(checkNotNull(address, "address"))) {
// Address type not supported by the resolver
return executor().newFailedFuture(new UnsupportedAddressTypeException());
}
if (isResolved(address)) {
// Resolved already; no need to perform a lookup
@SuppressWarnings("unchecked")
final T cast = (T) address;
return executor.newSucceededFuture(Collections.singletonList(cast));
}
try {
@SuppressWarnings("unchecked")
final T cast = (T) address;
final Promise<List<T>> promise = executor().newPromise();
doResolveAll(cast, promise);
return promise;
} catch (Exception e) {
return executor().newFailedFuture(e);
}
}
@Override
public final Future<List<T>> resolveAll(SocketAddress address, Promise<List<T>> promise) {
checkNotNull(address, "address");
checkNotNull(promise, "promise");
if (!isSupported(address)) {
// Address type not supported by the resolver
return promise.setFailure(new UnsupportedAddressTypeException());
}
if (isResolved(address)) {
// Resolved already; no need to perform a lookup
@SuppressWarnings("unchecked")
final T cast = (T) address;
return promise.setSuccess(Collections.singletonList(cast));
}
try {
@SuppressWarnings("unchecked")
final T cast = (T) address;
doResolveAll(cast, promise);
return promise;
} catch (Exception e) {
return promise.setFailure(e);
}
}
/**
* Invoked by {@link #resolve(SocketAddress)} to perform the actual name
* resolution.
*/
protected abstract void doResolve(T unresolvedAddress, Promise<T> promise) throws Exception;
/**
* Invoked by {@link #resolveAll(SocketAddress)} to perform the actual name
* resolution.
*/
protected abstract void doResolveAll(T unresolvedAddress, Promise<List<T>> promise) throws Exception;
@Override
public void close() { }
}

View File

@ -0,0 +1,90 @@
/*
* 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;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.io.Closeable;
import java.net.SocketAddress;
import java.nio.channels.UnsupportedAddressTypeException;
import java.util.List;
/**
* Resolves a possibility unresolved {@link {@link SocketAddress}}.
*/
public interface AddressResolver<T extends SocketAddress> extends Closeable {
/**
* Returns {@code true} if and only if the specified address is supported by this resolved.
*/
boolean isSupported(SocketAddress address);
/**
* Returns {@code true} if and only if the specified address has been resolved.
*
* @throws UnsupportedAddressTypeException if the specified address is not supported by this resolver
*/
boolean isResolved(SocketAddress address);
/**
* Resolves the specified address. If the specified address is resolved already, this method does nothing
* but returning the original address.
*
* @param address the address to resolve
*
* @return the {@link SocketAddress} as the result of the resolution
*/
Future<T> resolve(SocketAddress address);
/**
* Resolves the specified address. If the specified address is resolved already, this method does nothing
* but returning the original address.
*
* @param address the address to resolve
* @param promise the {@link Promise} which will be fulfilled when the name resolution is finished
*
* @return the {@link SocketAddress} as the result of the resolution
*/
Future<T> resolve(SocketAddress address, Promise<T> promise);
/**
* Resolves the specified address. If the specified address is resolved already, this method does nothing
* but returning the original address.
*
* @param address the address to resolve
*
* @return the list of the {@link SocketAddress}es as the result of the resolution
*/
Future<List<T>> resolveAll(SocketAddress address);
/**
* Resolves the specified address. If the specified address is resolved already, this method does nothing
* but returning the original address.
*
* @param address the address to resolve
* @param promise the {@link Promise} which will be fulfilled when the name resolution is finished
*
* @return the list of the {@link SocketAddress}es as the result of the resolution
*/
Future<List<T>> resolveAll(SocketAddress address, Promise<List<T>> promise);
/**
* Closes all the resources allocated and used by this resolver.
*/
@Override
void close();
}

View File

@ -31,25 +31,25 @@ import java.util.concurrent.ConcurrentMap;
/** /**
* Creates and manages {@link NameResolver}s so that each {@link EventExecutor} has its own resolver instance. * Creates and manages {@link NameResolver}s so that each {@link EventExecutor} has its own resolver instance.
*/ */
public abstract class NameResolverGroup<T extends SocketAddress> implements Closeable { public abstract class AddressResolverGroup<T extends SocketAddress> implements Closeable {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(NameResolverGroup.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(AddressResolverGroup.class);
/** /**
* Note that we do not use a {@link ConcurrentMap} here because it is usually expensive to instantiate a resolver. * Note that we do not use a {@link ConcurrentMap} here because it is usually expensive to instantiate a resolver.
*/ */
private final Map<EventExecutor, NameResolver<T>> resolvers = private final Map<EventExecutor, AddressResolver<T>> resolvers =
new IdentityHashMap<EventExecutor, NameResolver<T>>(); new IdentityHashMap<EventExecutor, AddressResolver<T>>();
protected NameResolverGroup() { } protected AddressResolverGroup() { }
/** /**
* Returns the {@link NameResolver} associated with the specified {@link EventExecutor}. If there's no associated * Returns the {@link AddressResolver} associated with the specified {@link EventExecutor}. If there's no associated
* resolved found, this method creates and returns a new resolver instance created by * resolved found, this method creates and returns a new resolver instance created by
* {@link #newResolver(EventExecutor)} so that the new resolver is reused on another * {@link #newResolver(EventExecutor)} so that the new resolver is reused on another
* {@link #getResolver(EventExecutor)} call with the same {@link EventExecutor}. * {@link #getResolver(EventExecutor)} call with the same {@link EventExecutor}.
*/ */
public NameResolver<T> getResolver(EventExecutor executor) { public AddressResolver<T> getResolver(final EventExecutor executor) {
if (executor == null) { if (executor == null) {
throw new NullPointerException("executor"); throw new NullPointerException("executor");
} }
@ -61,12 +61,12 @@ public abstract class NameResolverGroup<T extends SocketAddress> implements Clos
return getResolver0(executor.unwrap()); return getResolver0(executor.unwrap());
} }
private NameResolver<T> getResolver0(final EventExecutor executor) { private AddressResolver<T> getResolver0(final EventExecutor executor) {
NameResolver<T> r; AddressResolver<T> r;
synchronized (resolvers) { synchronized (resolvers) {
r = resolvers.get(executor); r = resolvers.get(executor);
if (r == null) { if (r == null) {
final NameResolver<T> newResolver; final AddressResolver<T> newResolver;
try { try {
newResolver = newResolver(executor); newResolver = newResolver(executor);
} catch (Exception e) { } catch (Exception e) {
@ -90,9 +90,9 @@ public abstract class NameResolverGroup<T extends SocketAddress> implements Clos
} }
/** /**
* Invoked by {@link #getResolver(EventExecutor)} to create a new {@link NameResolver}. * Invoked by {@link #getResolver(EventExecutor)} to create a new {@link AddressResolver}.
*/ */
protected abstract NameResolver<T> newResolver(EventExecutor executor) throws Exception; protected abstract AddressResolver<T> newResolver(EventExecutor executor) throws Exception;
/** /**
* Closes all {@link NameResolver}s created by this group. * Closes all {@link NameResolver}s created by this group.
@ -100,13 +100,13 @@ public abstract class NameResolverGroup<T extends SocketAddress> implements Clos
@Override @Override
@SuppressWarnings({ "unchecked", "SuspiciousToArrayCall" }) @SuppressWarnings({ "unchecked", "SuspiciousToArrayCall" })
public void close() { public void close() {
final NameResolver<T>[] rArray; final AddressResolver<T>[] rArray;
synchronized (resolvers) { synchronized (resolvers) {
rArray = (NameResolver<T>[]) resolvers.values().toArray(new NameResolver[resolvers.size()]); rArray = (AddressResolver<T>[]) resolvers.values().toArray(new AddressResolver[resolvers.size()]);
resolvers.clear(); resolvers.clear();
} }
for (NameResolver<T> r: rArray) { for (AddressResolver<T> r: rArray) {
try { try {
r.close(); r.close();
} catch (Throwable t) { } catch (Throwable t) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2014 The Netty Project * Copyright 2015 The Netty Project
* *
* The Netty Project licenses this file to you under the Apache License, * 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 * version 2.0 (the "License"); you may not use this file except in compliance
@ -21,16 +21,16 @@ import io.netty.util.concurrent.EventExecutor;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
/** /**
* A {@link NameResolverGroup} of {@link DefaultNameResolver}s. * A {@link AddressResolverGroup} of {@link DefaultNameResolver}s.
*/ */
public final class DefaultNameResolverGroup extends NameResolverGroup<InetSocketAddress> { public final class DefaultAddressResolverGroup extends AddressResolverGroup<InetSocketAddress> {
public static final DefaultNameResolverGroup INSTANCE = new DefaultNameResolverGroup(); public static final DefaultAddressResolverGroup INSTANCE = new DefaultAddressResolverGroup();
private DefaultNameResolverGroup() { } private DefaultAddressResolverGroup() { }
@Override @Override
protected NameResolver<InetSocketAddress> newResolver(EventExecutor executor) throws Exception { protected AddressResolver<InetSocketAddress> newResolver(EventExecutor executor) throws Exception {
return new DefaultNameResolver(executor); return new DefaultNameResolver(executor).asAddressResolver();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2014 The Netty Project * Copyright 2015 The Netty Project
* *
* The Netty Project licenses this file to you under the Apache License, * 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 * version 2.0 (the "License"); you may not use this file except in compliance
@ -22,49 +22,32 @@ import io.netty.util.concurrent.Promise;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
* A {@link NameResolver} that resolves an {@link InetSocketAddress} using JDK's built-in domain name lookup mechanism. * A {@link InetNameResolver} that resolves using JDK's built-in domain name lookup mechanism.
* Note that this resolver performs a blocking name lookup from the caller thread. * Note that this resolver performs a blocking name lookup from the caller thread.
*/ */
public class DefaultNameResolver extends SimpleNameResolver<InetSocketAddress> { public class DefaultNameResolver extends InetNameResolver {
public DefaultNameResolver(EventExecutor executor) { public DefaultNameResolver(EventExecutor executor) {
super(executor); super(executor);
} }
@Override @Override
protected boolean doIsResolved(InetSocketAddress address) { protected void doResolve(String inetHost, Promise<InetAddress> promise) throws Exception {
return !address.isUnresolved();
}
@Override
protected void doResolve(InetSocketAddress unresolvedAddress, Promise<InetSocketAddress> promise) throws Exception {
try { try {
// Note that InetSocketAddress.getHostName() will never incur a reverse lookup here, promise.setSuccess(InetAddress.getByName(inetHost));
// because an unresolved address always has a host name.
promise.setSuccess(new InetSocketAddress(
InetAddress.getByName(unresolvedAddress.getHostName()), unresolvedAddress.getPort()));
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
promise.setFailure(e); promise.setFailure(e);
} }
} }
@Override @Override
protected void doResolveAll( protected void doResolveAll(String inetHost, Promise<List<InetAddress>> promise) throws Exception {
InetSocketAddress unresolvedAddress, Promise<List<InetSocketAddress>> promise) throws Exception {
try { try {
// Note that InetSocketAddress.getHostName() will never incur a reverse lookup here, promise.setSuccess(Arrays.asList(InetAddress.getAllByName(inetHost)));
// because an unresolved address always has a host name.
final InetAddress[] resolved = InetAddress.getAllByName(unresolvedAddress.getHostName());
final List<InetSocketAddress> result = new ArrayList<InetSocketAddress>(resolved.length);
for (InetAddress a: resolved) {
result.add(new InetSocketAddress(a, unresolvedAddress.getPort()));
}
promise.setSuccess(result);
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
promise.setFailure(e); promise.setFailure(e);
} }

View File

@ -0,0 +1,54 @@
/*
* 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;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import java.net.InetAddress;
import java.net.InetSocketAddress;
/**
* A skeletal {@link NameResolver} implementation that resolves {@link InetAddress}.
*/
public abstract class InetNameResolver extends SimpleNameResolver<InetAddress> {
private volatile AddressResolver<InetSocketAddress> addressResolver;
/**
* @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned
* by {@link #resolve(String)}
*/
protected InetNameResolver(EventExecutor executor) {
super(executor);
}
/**
* Creates a new {@link AddressResolver} that will use this name resolver underneath.
*/
public AddressResolver<InetSocketAddress> asAddressResolver() {
AddressResolver<InetSocketAddress> result = addressResolver;
if (result == null) {
synchronized (this) {
result = addressResolver;
if (result == null) {
addressResolver = result = new InetSocketAddressResolver(executor(), this);
}
}
}
return result;
}
}

View File

@ -0,0 +1,91 @@
/*
* 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;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
/**
* A {@link AbstractAddressResolver} that resolves {@link InetAddress}.
*/
public class InetSocketAddressResolver extends AbstractAddressResolver<InetSocketAddress> {
private final NameResolver<InetAddress> nameResolver;
/**
* @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned
* by {@link #resolve(java.net.SocketAddress)}
* @param nameResolver the {@link NameResolver} used for name resolution
*/
public InetSocketAddressResolver(EventExecutor executor, NameResolver<InetAddress> nameResolver) {
super(executor, InetSocketAddress.class);
this.nameResolver = nameResolver;
}
@Override
protected boolean doIsResolved(InetSocketAddress address) {
return !address.isUnresolved();
}
@Override
protected void doResolve(final InetSocketAddress unresolvedAddress, final Promise<InetSocketAddress> promise)
throws Exception {
// Note that InetSocketAddress.getHostName() will never incur a reverse lookup here,
// because an unresolved address always has a host name.
nameResolver.resolve(unresolvedAddress.getHostName())
.addListener(new GenericFutureListener<Future<InetAddress>>() {
@Override
public void operationComplete(Future<InetAddress> future) throws Exception {
if (future.isSuccess()) {
promise.setSuccess(new InetSocketAddress(future.getNow(), unresolvedAddress.getPort()));
} else {
promise.setFailure(future.cause());
}
}
});
}
@Override
protected void doResolveAll(final InetSocketAddress unresolvedAddress,
final Promise<List<InetSocketAddress>> promise) throws Exception {
// Note that InetSocketAddress.getHostName() will never incur a reverse lookup here,
// because an unresolved address always has a host name.
nameResolver.resolveAll(unresolvedAddress.getHostName())
.addListener(new GenericFutureListener<Future<List<InetAddress>>>() {
@Override
public void operationComplete(Future<List<InetAddress>> future) throws Exception {
if (future.isSuccess()) {
List<InetAddress> inetAddresses = future.getNow();
List<InetSocketAddress> socketAddresses =
new ArrayList<InetSocketAddress>(inetAddresses.size());
for (InetAddress inetAddress : inetAddresses) {
socketAddresses.add(new InetSocketAddress(inetAddress, unresolvedAddress.getPort()));
}
promise.setSuccess(socketAddresses);
} else {
promise.setFailure(future.cause());
}
}
});
}
}

View File

@ -20,110 +20,50 @@ import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import java.io.Closeable; import java.io.Closeable;
import java.net.SocketAddress;
import java.nio.channels.UnsupportedAddressTypeException;
import java.util.List; import java.util.List;
/** /**
* Resolves an arbitrary string that represents the name of an endpoint into a {@link SocketAddress}. * Resolves an arbitrary string that represents the name of an endpoint into an address.
*/ */
public interface NameResolver<T extends SocketAddress> extends Closeable { public interface NameResolver<T> extends Closeable {
/** /**
* Returns {@code true} if and only if the specified address is supported by this resolved. * Resolves the specified name into an address.
*/
boolean isSupported(SocketAddress address);
/**
* Returns {@code true} if and only if the specified address has been resolved.
*
* @throws UnsupportedAddressTypeException if the specified address is not supported by this resolver
*/
boolean isResolved(SocketAddress address);
/**
* Resolves the specified name into a {@link SocketAddress}.
* *
* @param inetHost the name to resolve * @param inetHost the name to resolve
* @param inetPort the port number
* *
* @return the {@link SocketAddress} as the result of the resolution * @return the address as the result of the resolution
*/ */
Future<T> resolve(String inetHost, int inetPort); Future<T> resolve(String inetHost);
/** /**
* Resolves the specified name into a {@link SocketAddress}. * Resolves the specified name into an address.
* *
* @param inetHost the name to resolve * @param inetHost the name to resolve
* @param inetPort the port number
* @param promise the {@link Promise} which will be fulfilled when the name resolution is finished * @param promise the {@link Promise} which will be fulfilled when the name resolution is finished
* *
* @return the {@link SocketAddress} as the result of the resolution * @return the address as the result of the resolution
*/ */
Future<T> resolve(String inetHost, int inetPort, Promise<T> promise); Future<T> resolve(String inetHost, Promise<T> promise);
/** /**
* Resolves the specified address. If the specified address is resolved already, this method does nothing * Resolves the specified host name and port into a list of address.
* but returning the original address.
*
* @param address the address to resolve
*
* @return the {@link SocketAddress} as the result of the resolution
*/
Future<T> resolve(SocketAddress address);
/**
* Resolves the specified address. If the specified address is resolved already, this method does nothing
* but returning the original address.
*
* @param address the address to resolve
* @param promise the {@link Promise} which will be fulfilled when the name resolution is finished
*
* @return the {@link SocketAddress} as the result of the resolution
*/
Future<T> resolve(SocketAddress address, Promise<T> promise);
/**
* Resolves the specified host name and port into a list of {@link SocketAddress}es.
* *
* @param inetHost the name to resolve * @param inetHost the name to resolve
* @param inetPort the port number
* *
* @return the list of the {@link SocketAddress}es as the result of the resolution * @return the list of the address as the result of the resolution
*/ */
Future<List<T>> resolveAll(String inetHost, int inetPort); Future<List<T>> resolveAll(String inetHost);
/** /**
* Resolves the specified host name and port into a list of {@link SocketAddress}es. * Resolves the specified host name and port into a list of address.
* *
* @param inetHost the name to resolve * @param inetHost the name to resolve
* @param inetPort the port number
* @param promise the {@link Promise} which will be fulfilled when the name resolution is finished * @param promise the {@link Promise} which will be fulfilled when the name resolution is finished
* *
* @return the list of the {@link SocketAddress}es as the result of the resolution * @return the list of the address as the result of the resolution
*/ */
Future<List<T>> resolveAll(String inetHost, int inetPort, Promise<List<T>> promise); Future<List<T>> resolveAll(String inetHost, Promise<List<T>> promise);
/**
* Resolves the specified address. If the specified address is resolved already, this method does nothing
* but returning the original address.
*
* @param address the address to resolve
*
* @return the list of the {@link SocketAddress}es as the result of the resolution
*/
Future<List<T>> resolveAll(SocketAddress address);
/**
* Resolves the specified address. If the specified address is resolved already, this method does nothing
* but returning the original address.
*
* @param address the address to resolve
* @param promise the {@link Promise} which will be fulfilled when the name resolution is finished
*
* @return the list of the {@link SocketAddress}es as the result of the resolution
*/
Future<List<T>> resolveAll(SocketAddress address, Promise<List<T>> promise);
/** /**
* Closes all the resources allocated and used by this resolver. * Closes all the resources allocated and used by this resolver.

View File

@ -24,12 +24,12 @@ import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* A {@link NameResolver} that does not perform any resolution but always reports successful resolution. * A {@link AddressResolver} that does not perform any resolution but always reports successful resolution.
* This resolver is useful when name resolution is performed by a handler in a pipeline, such as a proxy handler. * This resolver is useful when name resolution is performed by a handler in a pipeline, such as a proxy handler.
*/ */
public class NoopNameResolver extends SimpleNameResolver<SocketAddress> { public class NoopAddressResolver extends AbstractAddressResolver<SocketAddress> {
public NoopNameResolver(EventExecutor executor) { public NoopAddressResolver(EventExecutor executor) {
super(executor); super(executor);
} }

View File

@ -21,16 +21,16 @@ import io.netty.util.concurrent.EventExecutor;
import java.net.SocketAddress; import java.net.SocketAddress;
/** /**
* A {@link NameResolverGroup} of {@link NoopNameResolver}s. * A {@link AddressResolverGroup} of {@link NoopAddressResolver}s.
*/ */
public final class NoopNameResolverGroup extends NameResolverGroup<SocketAddress> { public final class NoopAddressResolverGroup extends AddressResolverGroup<SocketAddress> {
public static final NoopNameResolverGroup INSTANCE = new NoopNameResolverGroup(); public static final NoopAddressResolverGroup INSTANCE = new NoopAddressResolverGroup();
private NoopNameResolverGroup() { } private NoopAddressResolverGroup() { }
@Override @Override
protected NameResolver<SocketAddress> newResolver(EventExecutor executor) throws Exception { protected AddressResolver<SocketAddress> newResolver(EventExecutor executor) throws Exception {
return new NoopNameResolver(executor); return new NoopAddressResolver(executor);
} }
} }

View File

@ -19,12 +19,7 @@ package io.netty.resolver;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import io.netty.util.internal.TypeParameterMatcher;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.UnsupportedAddressTypeException;
import java.util.Collections;
import java.util.List; import java.util.List;
import static io.netty.util.internal.ObjectUtil.*; import static io.netty.util.internal.ObjectUtil.*;
@ -32,124 +27,39 @@ import static io.netty.util.internal.ObjectUtil.*;
/** /**
* A skeletal {@link NameResolver} implementation. * A skeletal {@link NameResolver} implementation.
*/ */
public abstract class SimpleNameResolver<T extends SocketAddress> implements NameResolver<T> { public abstract class SimpleNameResolver<T> implements NameResolver<T> {
private final EventExecutor executor; private final EventExecutor executor;
private final TypeParameterMatcher matcher;
/** /**
* @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned * @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned
* by {@link #resolve(SocketAddress)} * by {@link #resolve(String)}
*/ */
protected SimpleNameResolver(EventExecutor executor) { protected SimpleNameResolver(EventExecutor executor) {
if (executor == null) { this.executor = checkNotNull(executor, "executor");
throw new NullPointerException("executor");
}
this.executor = executor;
matcher = TypeParameterMatcher.find(this, SimpleNameResolver.class, "T");
}
/**
* @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned
* by {@link #resolve(SocketAddress)}
* @param addressType the type of the {@link SocketAddress} supported by this resolver
*/
protected SimpleNameResolver(EventExecutor executor, Class<? extends T> addressType) {
if (executor == null) {
throw new NullPointerException("executor");
}
this.executor = executor;
matcher = TypeParameterMatcher.get(addressType);
} }
/** /**
* Returns the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned * Returns the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned
* by {@link #resolve(SocketAddress)}. * by {@link #resolve(String)}.
*/ */
protected EventExecutor executor() { protected EventExecutor executor() {
return executor; return executor;
} }
@Override @Override
public boolean isSupported(SocketAddress address) { public final Future<T> resolve(String inetHost) {
return matcher.match(address); final Promise<T> promise = executor().newPromise();
return resolve(inetHost, promise);
} }
@Override @Override
public final boolean isResolved(SocketAddress address) { public Future<T> resolve(String inetHost, Promise<T> promise) {
if (!isSupported(address)) { checkNotNull(inetHost, "inetHost");
throw new UnsupportedAddressTypeException();
}
@SuppressWarnings("unchecked")
final T castAddress = (T) address;
return doIsResolved(castAddress);
}
/**
* Invoked by {@link #isResolved(SocketAddress)} to check if the specified {@code address} has been resolved
* already.
*/
protected abstract boolean doIsResolved(T address);
@Override
public final Future<T> resolve(String inetHost, int inetPort) {
return resolve(InetSocketAddress.createUnresolved(checkNotNull(inetHost, "inetHost"), inetPort));
}
@Override
public Future<T> resolve(String inetHost, int inetPort, Promise<T> promise) {
return resolve(InetSocketAddress.createUnresolved(checkNotNull(inetHost, "inetHost"), inetPort), promise);
}
@Override
public final Future<T> resolve(SocketAddress address) {
if (!isSupported(checkNotNull(address, "address"))) {
// Address type not supported by the resolver
return executor().newFailedFuture(new UnsupportedAddressTypeException());
}
if (isResolved(address)) {
// Resolved already; no need to perform a lookup
@SuppressWarnings("unchecked")
final T cast = (T) address;
return executor.newSucceededFuture(cast);
}
try {
@SuppressWarnings("unchecked")
final T cast = (T) address;
final Promise<T> promise = executor().newPromise();
doResolve(cast, promise);
return promise;
} catch (Exception e) {
return executor().newFailedFuture(e);
}
}
@Override
public final Future<T> resolve(SocketAddress address, Promise<T> promise) {
checkNotNull(address, "address");
checkNotNull(promise, "promise"); checkNotNull(promise, "promise");
if (!isSupported(address)) {
// Address type not supported by the resolver
return promise.setFailure(new UnsupportedAddressTypeException());
}
if (isResolved(address)) {
// Resolved already; no need to perform a lookup
@SuppressWarnings("unchecked")
final T cast = (T) address;
return promise.setSuccess(cast);
}
try { try {
@SuppressWarnings("unchecked") doResolve(inetHost, promise);
final T cast = (T) address;
doResolve(cast, promise);
return promise; return promise;
} catch (Exception e) { } catch (Exception e) {
return promise.setFailure(e); return promise.setFailure(e);
@ -157,61 +67,18 @@ public abstract class SimpleNameResolver<T extends SocketAddress> implements Nam
} }
@Override @Override
public final Future<List<T>> resolveAll(String inetHost, int inetPort) { public final Future<List<T>> resolveAll(String inetHost) {
return resolveAll(InetSocketAddress.createUnresolved(checkNotNull(inetHost, "inetHost"), inetPort)); final Promise<List<T>> promise = executor().newPromise();
return resolveAll(inetHost, promise);
} }
@Override @Override
public Future<List<T>> resolveAll(String inetHost, int inetPort, Promise<List<T>> promise) { public Future<List<T>> resolveAll(String inetHost, Promise<List<T>> promise) {
return resolveAll(InetSocketAddress.createUnresolved(checkNotNull(inetHost, "inetHost"), inetPort), promise); checkNotNull(inetHost, "inetHost");
}
@Override
public final Future<List<T>> resolveAll(SocketAddress address) {
if (!isSupported(checkNotNull(address, "address"))) {
// Address type not supported by the resolver
return executor().newFailedFuture(new UnsupportedAddressTypeException());
}
if (isResolved(address)) {
// Resolved already; no need to perform a lookup
@SuppressWarnings("unchecked")
final T cast = (T) address;
return executor.newSucceededFuture(Collections.singletonList(cast));
}
try {
@SuppressWarnings("unchecked")
final T cast = (T) address;
final Promise<List<T>> promise = executor().newPromise();
doResolveAll(cast, promise);
return promise;
} catch (Exception e) {
return executor().newFailedFuture(e);
}
}
@Override
public final Future<List<T>> resolveAll(SocketAddress address, Promise<List<T>> promise) {
checkNotNull(address, "address");
checkNotNull(promise, "promise"); checkNotNull(promise, "promise");
if (!isSupported(address)) {
// Address type not supported by the resolver
return promise.setFailure(new UnsupportedAddressTypeException());
}
if (isResolved(address)) {
// Resolved already; no need to perform a lookup
@SuppressWarnings("unchecked")
final T cast = (T) address;
return promise.setSuccess(Collections.singletonList(cast));
}
try { try {
@SuppressWarnings("unchecked") doResolveAll(inetHost, promise);
final T cast = (T) address;
doResolveAll(cast, promise);
return promise; return promise;
} catch (Exception e) { } catch (Exception e) {
return promise.setFailure(e); return promise.setFailure(e);
@ -219,16 +86,14 @@ public abstract class SimpleNameResolver<T extends SocketAddress> implements Nam
} }
/** /**
* Invoked by {@link #resolve(SocketAddress)} and {@link #resolve(String, int)} to perform the actual name * Invoked by {@link #resolve(String)} to perform the actual name resolution.
* resolution.
*/ */
protected abstract void doResolve(T unresolvedAddress, Promise<T> promise) throws Exception; protected abstract void doResolve(String inetHost, Promise<T> promise) throws Exception;
/** /**
* Invoked by {@link #resolveAll(SocketAddress)} and {@link #resolveAll(String, int)} to perform the actual name * Invoked by {@link #resolveAll(String)} to perform the actual name resolution.
* resolution.
*/ */
protected abstract void doResolveAll(T unresolvedAddress, Promise<List<T>> promise) throws Exception; protected abstract void doResolveAll(String inetHost, Promise<List<T>> promise) throws Exception;
@Override @Override
public void close() { } public void close() { }

View File

@ -15,6 +15,6 @@
*/ */
/** /**
* Resolves an arbitrary string that represents the name of an endpoint into a {@link java.net.SocketAddress}. * Resolves an arbitrary string that represents the name of an endpoint into an address.
*/ */
package io.netty.resolver; package io.netty.resolver;

View File

@ -23,9 +23,10 @@ import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.resolver.DefaultNameResolverGroup; import io.netty.resolver.AddressResolver;
import io.netty.resolver.DefaultAddressResolverGroup;
import io.netty.resolver.NameResolver; import io.netty.resolver.NameResolver;
import io.netty.resolver.NameResolverGroup; import io.netty.resolver.AddressResolverGroup;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.FutureListener;
@ -50,10 +51,11 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Bootstrap.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(Bootstrap.class);
private static final NameResolverGroup<?> DEFAULT_RESOLVER = DefaultNameResolverGroup.INSTANCE; private static final AddressResolverGroup<?> DEFAULT_RESOLVER = DefaultAddressResolverGroup.INSTANCE;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private volatile NameResolverGroup<SocketAddress> resolver = (NameResolverGroup<SocketAddress>) DEFAULT_RESOLVER; private volatile AddressResolverGroup<SocketAddress> resolver =
(AddressResolverGroup<SocketAddress>) DEFAULT_RESOLVER;
private volatile SocketAddress remoteAddress; private volatile SocketAddress remoteAddress;
public Bootstrap() { } public Bootstrap() { }
@ -68,11 +70,11 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
* Sets the {@link NameResolver} which will resolve the address of the unresolved named address. * Sets the {@link NameResolver} which will resolve the address of the unresolved named address.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Bootstrap resolver(NameResolverGroup<?> resolver) { public Bootstrap resolver(AddressResolverGroup<?> resolver) {
if (resolver == null) { if (resolver == null) {
throw new NullPointerException("resolver"); throw new NullPointerException("resolver");
} }
this.resolver = (NameResolverGroup<SocketAddress>) resolver; this.resolver = (AddressResolverGroup<SocketAddress>) resolver;
return this; return this;
} }
@ -162,7 +164,7 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
final Channel channel = regFuture.channel(); final Channel channel = regFuture.channel();
final EventLoop eventLoop = channel.eventLoop(); final EventLoop eventLoop = channel.eventLoop();
final NameResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop); final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);
if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) { if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
// Resolver has no idea about what to do with the specified remote address or it's resolved already. // Resolver has no idea about what to do with the specified remote address or it's resolved already.

View File

@ -30,9 +30,9 @@ import io.netty.channel.ServerChannel;
import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalServerChannel; import io.netty.channel.local.LocalServerChannel;
import io.netty.resolver.NameResolver; import io.netty.resolver.AddressResolver;
import io.netty.resolver.NameResolverGroup; import io.netty.resolver.AddressResolverGroup;
import io.netty.resolver.SimpleNameResolver; import io.netty.resolver.AbstractAddressResolver;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
@ -221,7 +221,7 @@ public class BootstrapTest {
final Bootstrap bootstrapA = new Bootstrap(); final Bootstrap bootstrapA = new Bootstrap();
bootstrapA.group(groupA); bootstrapA.group(groupA);
bootstrapA.channel(LocalChannel.class); bootstrapA.channel(LocalChannel.class);
bootstrapA.resolver(new TestNameResolverGroup(true)); bootstrapA.resolver(new TestAddressResolverGroup(true));
bootstrapA.handler(dummyHandler); bootstrapA.handler(dummyHandler);
final ServerBootstrap bootstrapB = new ServerBootstrap(); final ServerBootstrap bootstrapB = new ServerBootstrap();
@ -240,7 +240,7 @@ public class BootstrapTest {
final Bootstrap bootstrapA = new Bootstrap(); final Bootstrap bootstrapA = new Bootstrap();
bootstrapA.group(groupA); bootstrapA.group(groupA);
bootstrapA.channel(LocalChannel.class); bootstrapA.channel(LocalChannel.class);
bootstrapA.resolver(new TestNameResolverGroup(false)); bootstrapA.resolver(new TestAddressResolverGroup(false));
bootstrapA.handler(dummyHandler); bootstrapA.handler(dummyHandler);
final ServerBootstrap bootstrapB = new ServerBootstrap(); final ServerBootstrap bootstrapB = new ServerBootstrap();
@ -282,17 +282,17 @@ public class BootstrapTest {
@Sharable @Sharable
private static final class DummyHandler extends ChannelHandlerAdapter { } private static final class DummyHandler extends ChannelHandlerAdapter { }
private static final class TestNameResolverGroup extends NameResolverGroup<SocketAddress> { private static final class TestAddressResolverGroup extends AddressResolverGroup<SocketAddress> {
private final boolean success; private final boolean success;
TestNameResolverGroup(boolean success) { TestAddressResolverGroup(boolean success) {
this.success = success; this.success = success;
} }
@Override @Override
protected NameResolver<SocketAddress> newResolver(EventExecutor executor) throws Exception { protected AddressResolver<SocketAddress> newResolver(EventExecutor executor) throws Exception {
return new SimpleNameResolver<SocketAddress>(executor) { return new AbstractAddressResolver<SocketAddress>(executor) {
@Override @Override
protected boolean doIsResolved(SocketAddress address) { protected boolean doIsResolved(SocketAddress address) {