The logic right now is like this:
public ReactorClientHttpConnector(ReactorResourceFactory factory, Function<HttpClient, HttpClient> mapper) {
this.httpClient = defaultInitializer.andThen(mapper).apply(initHttpClient(factory));
}
@SuppressWarnings("deprecation")
private static HttpClient initHttpClient(ReactorResourceFactory resourceFactory) {
ConnectionProvider provider = resourceFactory.getConnectionProvider();
LoopResources resources = resourceFactory.getLoopResources();
Assert.notNull(provider, "No ConnectionProvider: is ReactorResourceFactory not initialized yet?");
Assert.notNull(resources, "No LoopResources: is ReactorResourceFactory not initialized yet?");
return HttpClient.create(provider).tcpConfiguration(tcpClient -> tcpClient.runOn(resources));
}
And that mapper
is called when tcpConfiguration()
has done its logic.
In the native image on Windows it fails like:
Caused by: java.lang.ExceptionInInitializerError
at io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder$1.provider(DnsServerAddressStreamProviders.java:140)
at io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder$1.<init>(DnsServerAddressStreamProviders.java:120)
at io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder.<clinit>(DnsServerAddressStreamProviders.java:118)
at io.netty.resolver.dns.DnsServerAddressStreamProviders.unixDefault(DnsServerAddressStreamProviders.java:107)
at io.netty.resolver.dns.DnsServerAddressStreamProviders.platformDefault(DnsServerAddressStreamProviders.java:103)
at io.netty.resolver.dns.DnsNameResolverBuilder.<init>(DnsNameResolverBuilder.java:60)
at reactor.netty.transport.NameResolverProvider.newNameResolverGroup(NameResolverProvider.java:455)
at reactor.netty.transport.ClientTransportConfig.lambda$getOrCreateResolver$0(ClientTransportConfig.java:239)
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1705)
at reactor.netty.transport.ClientTransportConfig.getOrCreateResolver(ClientTransportConfig.java:238)
at reactor.netty.transport.ClientTransport.runOn(ClientTransport.java:352)
at reactor.netty.transport.ClientTransport.runOn(ClientTransport.java:42)
at reactor.netty.transport.Transport.runOn(Transport.java:249)
at reactor.netty.http.client.HttpClientTcpConfig.runOn(HttpClientTcpConfig.java:189)
at org.springframework.http.client.reactive.ReactorClientHttpConnector.lambda$initHttpClient$1(ReactorClientHttpConnector.java:85)
at reactor.netty.http.client.HttpClient.tcpConfiguration(HttpClient.java:1494)
at org.springframework.http.client.reactive.ReactorClientHttpConnector.initHttpClient(ReactorClientHttpConnector.java:85)
at org.springframework.http.client.reactive.ReactorClientHttpConnector.<init>(ReactorClientHttpConnector.java:76)
at org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorConfiguration$ReactorNetty.reactorClientHttpConnector(ClientHttpConnectorConfiguration.java:66)
at org.springframework.boot.autoconfigure.web.reactive.function.client.ContextBootstrapInitializer.lambda$registerReactorNetty_reactorClientHttpConnector$0(ContextBootstrapInitializer.java:19)
at org.springframework.aot.beans.factory.ThrowableFunction.apply(ThrowableFunction.java:18)
at org.springframework.aot.beans.factory.InjectedElementResolver.create(InjectedElementResolver.java:51)
at org.springframework.aot.beans.factory.BeanDefinitionRegistrar$InstanceSupplierContext.create(BeanDefinitionRegistrar.java:193)
at org.springframework.boot.autoconfigure.web.reactive.function.client.ContextBootstrapInitializer.lambda$registerReactorNetty_reactorClientHttpConnector$1(ContextBootstrapInitializer.java:19)
at org.springframework.aot.beans.factory.ThrowableFunction.apply(ThrowableFunction.java:18)
at org.springframework.aot.beans.factory.BeanDefinitionRegistrar.lambda$instanceSupplier$0(BeanDefinitionRegistrar.java:97)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1249)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
... 52 more
Caused by: java.lang.NullPointerException
at java.util.StringTokenizer.<init>(StringTokenizer.java:199)
at java.util.StringTokenizer.<init>(StringTokenizer.java:221)
at sun.net.dns.ResolverConfigurationImpl.stringToList(ResolverConfigurationImpl.java:69)
at sun.net.dns.ResolverConfigurationImpl.loadConfig(ResolverConfigurationImpl.java:104)
at sun.net.dns.ResolverConfigurationImpl.nameservers(ResolverConfigurationImpl.java:127)
at com.sun.jndi.dns.DnsContextFactory.serversForUrls(DnsContextFactory.java:149)
at com.sun.jndi.dns.DnsContextFactory.getContext(DnsContextFactory.java:81)
at com.sun.jndi.dns.DnsContextFactory.urlToContext(DnsContextFactory.java:120)
at com.sun.jndi.dns.DnsContextFactory.getInitialContext(DnsContextFactory.java:64)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:730)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305)
at javax.naming.InitialContext.init(InitialContext.java:236)
at javax.naming.InitialContext.<init>(InitialContext.java:208)
at javax.naming.directory.InitialDirContext.<init>(InitialDirContext.java:101)
at io.netty.resolver.dns.DirContextUtils.addNameServers(DirContextUtils.java:49)
at io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.<clinit>(DefaultDnsServerAddressStreamProvider.java:53)
... 82 more
Even if I provide this customizer:
@Bean
ReactorNettyHttpClientMapper reactorNettyHttpClientMapper() {
return httpClient -> httpClient.resolver(DefaultAddressResolverGroup.INSTANCE);
}
In my opinion it has to be like this:
HttpClient httpClient =
mapper.apply(HttpClient.create(provider))
.tcpConfiguration(tcpClient -> tcpClient.runOn(resources));
This one works for me in my Spring Boot application:
@Bean
ReactorResourceFactory reactorClientResourceFactory() {
return new ReactorResourceFactory();
}
@Bean
ReactorClientHttpConnector reactorClientHttpConnector(ReactorResourceFactory reactorResourceFactory) {
ConnectionProvider provider = reactorResourceFactory.getConnectionProvider();
LoopResources resources = reactorResourceFactory.getLoopResources();
Function<HttpClient, HttpClient> mapper =
httpClient -> httpClient.resolver(DefaultAddressResolverGroup.INSTANCE);
HttpClient httpClient =
mapper.apply(HttpClient.create(provider))
.tcpConfiguration(tcpClient -> tcpClient.runOn(resources));
return new ReactorClientHttpConnector(httpClient);
}
See more info in this Reactor Netty issue: https://github.com/reactor/reactor-netty/issues/1431
Comment From: rstoyanchev
Given that ResourceFactory
is passed in, clearly the intent is to apply it. I think it's okay to move it and have it applied after the provided mapper. The two shouldn't be competing with each other.