Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The ReactorClientHttpConnector must apply mapper before tcpConfiguration() #27749

Closed
artembilan opened this issue Nov 29, 2021 · 1 comment
Closed
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Milestone

Comments

@artembilan
Copy link
Member

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: reactor/reactor-netty#1431

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Nov 29, 2021
@jhoeller jhoeller added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Nov 29, 2021
@jhoeller jhoeller added this to the Triage Queue milestone Nov 29, 2021
artembilan added a commit to artembilan/spring-native that referenced this issue Nov 29, 2021
Fixes spring-attic#1134

* Upgrade sample to the latest Spring Integration `5.5.7-SNAPSHOT`
* Add `maven-shade-plugin` and `native-maven-plugin` under Windows profile
to make native image build working on Windows.
Se more info in Native Build Tools docs:
https://graalvm.github.io/native-build-tools/latest/maven-plugin.html#long_classpath_and_shading_support
This profile simply can go to the `../maven-parent/pom.xml` to make all the samples working on Windows.
Or you can remove this on merge since it might be just a noise for a broader community
* Add custom `ReactorClientHttpConnector` bean into the sample app, since by default a DNS resolver
fails with NPE on Windows in native image.
See more info in the: spring-projects/spring-framework#27749
* Add more type nints into the `IntegrationHints`
@rstoyanchev rstoyanchev self-assigned this Nov 30, 2021
@rstoyanchev
Copy link
Contributor

rstoyanchev commented Nov 30, 2021

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants