netty: Missing graalvm native image reflection hints for SelectorProvider

Expected behavior

Netty should cover all graalvm native image reflection hints

Actual behavior

https://github.com/netty/netty/blob/4.1/transport/src/main/resources/META-INF/native-image/io.netty/netty-transport/reflection-config.json

Although there are configuration for java.lang.management.ManagementFactory and java.lang.management.RuntimeMXBean etc. But not include java.nio.channels.spi.SelectorProvide which is invoke by reflection in SelectorProviderUtil

Netty code in io.netty.channel.socket.nio.SelectorProviderUtil:

@SuppressJava6Requirement(reason = "Usage guarded by java version check")
static Method findOpenMethod(String methodName) {
    if (PlatformDependent.javaVersion() >= 15) {
        try {
            return SelectorProvider.class.getMethod(methodName, java.net.ProtocolFamily.class);
        } catch (Throwable e) {
            logger.debug("SelectorProvider.{}(ProtocolFamily) not available, will use default", methodName, e);
        }
    }
    return null;
}

In fact, Netty will use SelectorProviderUtil.findOpenMethod to find and invoke reflective two methods in SelectorProvider in a Spring application:

public SocketChannel openSocketChannel(ProtocolFamily family)
public ServerSocketChannel openServerSocketChannel(ProtocolFamily family)

Steps to reproduce

After compiled a Spring application to graalvm native image, and set log level for io.netty to TRACE, such exception will show up(which can’t reproduce in jvm application):

2023-08-31T01:27:05.755+08:00 TRACE 55266 --- [           main] io.netty.channel.nio.NioEventLoop        : instrumented a special java.util.Set into: sun.nio.ch.EPollSelectorImpl@28fee399
2023-08-31T01:27:05.755+08:00 DEBUG 55266 --- [           main] i.n.c.socket.nio.SelectorProviderUtil    : SelectorProvider.openServerSocketChannel(ProtocolFamily) not available, will use default

java.lang.NoSuchMethodException: java.nio.channels.spi.SelectorProvider.openServerSocketChannel(java.net.ProtocolFamily)
        at java.base@17.0.8/java.lang.Class.checkMethod(DynamicHub.java:1038) ~[monolith-service:na]
        at java.base@17.0.8/java.lang.Class.getMethod(DynamicHub.java:1023) ~[monolith-service:na]
        at io.netty.channel.socket.nio.SelectorProviderUtil.findOpenMethod(SelectorProviderUtil.java:39) ~[na:na]
        at io.netty.channel.socket.nio.NioServerSocketChannel.<clinit>(NioServerSocketChannel.java:57) ~[monolith-service:4.1.97.Final]
        at reactor.netty.resources.DefaultLoopNIO.getChannel(DefaultLoopNIO.java:45) ~[na:na]
        at reactor.netty.resources.LoopResources.onChannel(LoopResources.java:243) ~[monolith-service:1.1.10]
        at reactor.netty.tcp.TcpResources.onChannel(TcpResources.java:251) ~[monolith-service:1.1.10]
        at reactor.netty.transport.TransportConfig.lambda$connectionFactory$1(TransportConfig.java:277) ~[monolith-service:1.1.10]
        at reactor.netty.transport.TransportConnector.doInitAndRegister(TransportConnector.java:277) ~[na:na]
        at reactor.netty.transport.TransportConnector.bind(TransportConnector.java:87) ~[na:na]
        at reactor.netty.transport.ServerTransport.lambda$bind$0(ServerTransport.java:115) ~[monolith-service:1.1.10]
        at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:58) ~[na:na]
        at reactor.core.publisher.Mono.subscribe(Mono.java:4495) ~[monolith-service:3.5.9]
        at reactor.core.publisher.Mono.block(Mono.java:1737) ~[monolith-service:3.5.9]
        at reactor.netty.transport.ServerTransport.bindNow(ServerTransport.java:149) ~[monolith-service:1.1.10]
        at reactor.netty.transport.ServerTransport.bindNow(ServerTransport.java:134) ~[monolith-service:1.1.10]
        at org.springframework.boot.web.embedded.netty.NettyWebServer.startHttpServer(NettyWebServer.java:145) ~[monolith-service:3.1.3]
        at org.springframework.boot.web.embedded.netty.NettyWebServer.start(NettyWebServer.java:100) ~[monolith-service:3.1.3]
        at org.springframework.boot.web.reactive.context.WebServerManager.start(WebServerManager.java:55) ~[na:na]
        at org.springframework.boot.web.reactive.context.WebServerStartStopLifecycle.start(WebServerStartStopLifecycle.java:41) ~[na:na]
        at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:179) ~[monolith-service:6.0.11]
        at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:357) ~[monolith-service:6.0.11]
        at java.base@17.0.8/java.lang.Iterable.forEach(Iterable.java:75) ~[monolith-service:na]
        at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:156) ~[monolith-service:6.0.11]
        at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:124) ~[monolith-service:6.0.11]
        at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:958) ~[monolith-service:6.0.11]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:611) ~[monolith-service:6.0.11]
        at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:66) ~[monolith-service:3.1.3]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) ~[monolith-service:3.1.3]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:436) ~[monolith-service:3.1.3]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) ~[monolith-service:3.1.3]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[monolith-service:3.1.3]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[monolith-service:3.1.3]
        at com.hkmci.masterspace.monolith.ApplicationKt.main(Application.kt:15) ~[monolith-service:na]
        at com.hkmci.masterspace.monolith.ApplicationKt.main(Application.kt) ~[monolith-service:na]

Minimal yet complete reproducer code (or URL to code)

Create a empty Webflux Spring application with Spring Initializr and compile to native image.

set log level of io.netty to TRACE.

Just run the application, the exception above will show up.

Netty version

4.1.97.Final

JVM version (e.g. java -version)

GraalVM for JDK 17 Community 17.0.8(native image)

OS version (e.g. uname -a)

Ubuntu22.04

About this issue

  • Original URL
  • State: closed
  • Created 10 months ago
  • Comments: 15 (9 by maintainers)

Most upvoted comments

I create PR in graalvm-reachability-metadata: https://github.com/oracle/graalvm-reachability-metadata/pull/382

@czp3009 I think this should be enough, can you test it?

{
  "condition":{"typeReachable":"io.netty.channel.socket.nio.NioServerSocketChannel"},
  "name":"java.nio.channels.spi.SelectorProvider",
  "methods":[
    {"name":"openServerSocketChannel","parameterTypes":["java.net.ProtocolFamily"] }
  ]
},
{
  "condition":{"typeReachable":"io.netty.channel.socket.nio.NioSocketChannel"},
  "name":"java.nio.channels.spi.SelectorProvider",
  "methods":[
    {"name":"openSocketChannel","parameterTypes":["java.net.ProtocolFamily"] }
  ]
}