Skip to content

Commit

Permalink
Make transport.Bootstrap usable with no netty-resolver on classpath (#…
Browse files Browse the repository at this point in the history
…13488)

Motivation:
Since some address families (e.g. unix sockets) do not need name
resolver, It should be possible to exclude netty-resolver from
classpath.

Currently excluding name resolver leads to NoClassDefFoundError:
AddressResolverGroup during Bootstrap instantiation.

Modification:

Add disableResolver() method to Bootstrap.

Change BootstrapConfig.resolver() return null if
Bootstrap.disableResolver() is called.

Introduce ExternalAddressResolver class to hold AddressResolver related
references to avoid NoClassDefFoundError if Bootstrap.disableResolver()
called and no netty-resolver is on classpath.

Result:

Bootstrap is usable with no netty-resolver on classpath.

---------

Co-authored-by: Chris Vest <mr.chrisvest@gmail.com>
  • Loading branch information
mostroverkhov and chrisvest committed Jul 18, 2023
1 parent ac34240 commit fa2c81f
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 11 deletions.
55 changes: 46 additions & 9 deletions transport/src/main/java/io/netty/bootstrap/Bootstrap.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,18 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {

private static final InternalLogger logger = InternalLoggerFactory.getInstance(Bootstrap.class);

private static final AddressResolverGroup<?> DEFAULT_RESOLVER = DefaultAddressResolverGroup.INSTANCE;

private final BootstrapConfig config = new BootstrapConfig(this);

@SuppressWarnings("unchecked")
private volatile AddressResolverGroup<SocketAddress> resolver =
(AddressResolverGroup<SocketAddress>) DEFAULT_RESOLVER;
private ExternalAddressResolver externalResolver;
private volatile boolean disableResolver;
private volatile SocketAddress remoteAddress;

public Bootstrap() { }

private Bootstrap(Bootstrap bootstrap) {
super(bootstrap);
resolver = bootstrap.resolver;
externalResolver = bootstrap.externalResolver;
disableResolver = bootstrap.disableResolver;
remoteAddress = bootstrap.remoteAddress;
}

Expand All @@ -74,7 +72,18 @@ private Bootstrap(Bootstrap bootstrap) {
*/
@SuppressWarnings("unchecked")
public Bootstrap resolver(AddressResolverGroup<?> resolver) {
this.resolver = (AddressResolverGroup<SocketAddress>) (resolver == null ? DEFAULT_RESOLVER : resolver);
this.externalResolver = resolver == null ? null : new ExternalAddressResolver(resolver);
disableResolver = false;
return this;
}

/**
* Disables address name resolution. Name resolution may be re-enabled with
* {@link Bootstrap#resolver(AddressResolverGroup)}
*/
public Bootstrap disableResolver() {
externalResolver = null;
disableResolver = true;
return this;
}

Expand Down Expand Up @@ -188,10 +197,15 @@ public void operationComplete(ChannelFuture future) throws Exception {
private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
final SocketAddress localAddress, final ChannelPromise promise) {
try {
if (disableResolver) {
doConnect(remoteAddress, localAddress, promise);
return promise;
}

final EventLoop eventLoop = channel.eventLoop();
AddressResolver<SocketAddress> resolver;
try {
resolver = this.resolver.getResolver(eventLoop);
resolver = ExternalAddressResolver.getOrDefault(externalResolver).getResolver(eventLoop);
} catch (Throwable cause) {
channel.close();
return promise.setFailure(cause);
Expand Down Expand Up @@ -301,6 +315,29 @@ final SocketAddress remoteAddress() {
}

final AddressResolverGroup<?> resolver() {
return resolver;
if (disableResolver) {
return null;
}
return ExternalAddressResolver.getOrDefault(externalResolver);
}

/* Holder to avoid NoClassDefFoundError in case netty-resolver dependency is excluded
(e.g. some address families do not need name resolution) */
static final class ExternalAddressResolver {
final AddressResolverGroup<SocketAddress> resolverGroup;

@SuppressWarnings("unchecked")
ExternalAddressResolver(AddressResolverGroup<?> resolverGroup) {
this.resolverGroup = (AddressResolverGroup<SocketAddress>) resolverGroup;
}

@SuppressWarnings("unchecked")
static AddressResolverGroup<SocketAddress> getOrDefault(ExternalAddressResolver externalResolver) {
if (externalResolver == null) {
AddressResolverGroup<?> defaultResolverGroup = DefaultAddressResolverGroup.INSTANCE;
return (AddressResolverGroup<SocketAddress>) defaultResolverGroup;
}
return externalResolver.resolverGroup;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public SocketAddress remoteAddress() {
}

/**
* Returns the configured {@link AddressResolverGroup} or the default if non is configured yet.
* Returns the configured {@link AddressResolverGroup}, {@code null} if resolver was disabled
* with {@link Bootstrap#disableResolver()}, or the default if not configured yet.
*/
public AddressResolverGroup<?> resolver() {
return bootstrap.resolver();
Expand All @@ -47,7 +48,11 @@ public AddressResolverGroup<?> resolver() {
public String toString() {
StringBuilder buf = new StringBuilder(super.toString());
buf.setLength(buf.length() - 1);
buf.append(", resolver: ").append(resolver());
AddressResolverGroup<?> resolver = resolver();
if (resolver != null) {
buf.append(", resolver: ")
.append(resolver);
}
SocketAddress remoteAddress = remoteAddress();
if (remoteAddress != null) {
buf.append(", remoteAddress: ")
Expand Down
25 changes: 25 additions & 0 deletions transport/src/test/java/io/netty/bootstrap/BootstrapTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import io.netty.resolver.AddressResolver;
import io.netty.resolver.AddressResolverGroup;
import io.netty.resolver.AbstractAddressResolver;
import io.netty.resolver.DefaultAddressResolverGroup;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
Expand Down Expand Up @@ -69,6 +70,8 @@
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -299,6 +302,24 @@ public void execute() {
}
}

@Test
void testResolverDefault() throws Exception {
Bootstrap bootstrap = new Bootstrap();

assertTrue(bootstrap.config().toString().contains("resolver:"));
assertNotNull(bootstrap.config().resolver());
assertEquals(DefaultAddressResolverGroup.class, bootstrap.config().resolver().getClass());
}

@Test
void testResolverDisabled() throws Exception {
Bootstrap bootstrap = new Bootstrap();
bootstrap.disableResolver();

assertFalse(bootstrap.config().toString().contains("resolver:"));
assertNull(bootstrap.config().resolver());
}

@Test
public void testAsyncResolutionSuccess() throws Exception {
final Bootstrap bootstrapA = new Bootstrap();
Expand All @@ -311,6 +332,10 @@ public void testAsyncResolutionSuccess() throws Exception {
bootstrapB.group(groupB);
bootstrapB.channel(LocalServerChannel.class);
bootstrapB.childHandler(dummyHandler);

assertTrue(bootstrapA.config().toString().contains("resolver:"));
assertThat(bootstrapA.resolver(), is(instanceOf(TestAddressResolverGroup.class)));

SocketAddress localAddress = bootstrapB.bind(LocalAddress.ANY).sync().channel().localAddress();

// Connect to the server using the asynchronous resolver.
Expand Down

0 comments on commit fa2c81f

Please sign in to comment.