Hello,

I have an application that I am trying to build my application into a native image. It has a Netty-based TCP server that I included in a bean. The application runs well with AOT. However, the native-image crashed at startup when trying to start the TCP server with the following exception:

Caused by: java.lang.NoClassDefFoundError: Could not initialize class io.netty.buffer.ByteBufAllocator
   at io.netty.channel.DefaultChannelConfig.<init>(DefaultChannelConfig.java:60)
   at io.netty.channel.DefaultChannelConfig.<init>(DefaultChannelConfig.java:75)
   at io.netty.bootstrap.FailedChannel.<init>(FailedChannel.java:30)
   at io.netty.bootstrap.AbstractBootstrap.initAndRegister(AbstractBootstrap.java:320)
   at io.netty.bootstrap.AbstractBootstrap.doBind(AbstractBootstrap.java:272)
   at io.netty.bootstrap.AbstractBootstrap.bind(AbstractBootstrap.java:268)
   at io.netty.bootstrap.AbstractBootstrap.bind(AbstractBootstrap.java:253)
   at my.app.TCPServer.run(TCPServer.java:135)
   at my.app.TCPServer.initialize(TCPServer.java:33)
   at java.base@17.0.9/java.lang.reflect.Method.invoke(Method.java:568)
   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1874)
   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1827)
   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1766)
   ... 32 common frames omitted

I am using Spring Boot 3.1.5 and Netty 4.1.100.Final. I am building using GraalVM Community Edition JDK 17.0.9.

I used the following configuration in my pom.xml:

<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
    <configuration>
        <skipNativeTests>true</skipNativeTests>
        <buildArgs>
            <buildArg>-H:-CheckToolchain</buildArg>
            <buildArg>-H:+ExhaustiveHeapScan</buildArg>
            <buildArg>-H:+UseExperimentalReachabilityAnalysis</buildArg>
            <buildArg>--static</buildArg>
            <buildArg>--configure-reflection-metadata</buildArg>
            <buildArg>--initialize-at-build-time="kotlin,kotlin._Assertions,kotlin.LazyKt__LazyJVMKt$WhenMappings,kotlin.SafePublicationLazyImpl,kotlin.KotlinVersion,kotlin.jvm,kotlin.reflect.jvm,org.slf4j.LoggerFactory,ch.qos.logback,net.logstash.logback,com.fasterxml.jackson,org.yaml.snakeyaml,org.codehaus.stax2,org.codehaus.stax2.typed.Base64Variants"</buildArg>
        </buildArgs>
    </configuration>
</plugin>

When I inspected the generated native-image argument file, it contained the following lines excluding Netty's own configuration:

--exclude-config
'\\QC:\\Users\\USERNAME\\.m2\\repository\\io\\netty\\netty-common\\4.1.100.Final\\netty-common-4.1.100.Final.jar\\E'
'^/META-INF/native-image/'
--exclude-config
'\\QC:\\Users\\USERNAME\\.m2\\repository\\io\\netty\\netty-handler\\4.1.100.Final\\netty-handler-4.1.100.Final.jar\\E'
'^/META-INF/native-image/'
--exclude-config
'\\QC:\\Users\\USERNAME\\.m2\\repository\\io\\netty\\netty-buffer\\4.1.100.Final\\netty-buffer-4.1.100.Final.jar\\E'
'^/META-INF/native-image/'
--exclude-config
'\\QC:\\Users\\USERNAME\\.m2\\repository\\io\\netty\\netty-transport\\4.1.100.Final\\netty-transport-4.1.100.Final.jar\\E'
'^/META-INF/native-image/'

I suspected that due to the configurations being excluded, GraalVM was unable to find the required classes at build time.

I added these arguments per this PR and it didn't crash anymore.

<buildArg>--initialize-at-build-time=io.netty</buildArg>
<buildArg>--initialize-at-run-time=io.netty.buffer.PooledByteBufAllocator,io.netty.buffer.ByteBufAllocator,io.netty.buffer.ByteBufUtil,io.netty.buffer.AbstractReferenceCountedByteBuf</buildArg>
<buildArg>--initialize-at-run-time=io.netty.util.AbstractReferenceCounted</buildArg>
<buildArg>--initialize-at-run-time=io.netty.handler.ssl.util.ThreadLocalInsecureRandom</buildArg>

Why does the Netty configuration get ignored?

Any help would be appreciated! Thanks!

Comment From: wilkinsona

You appear to be using Netty directly which takes things out of our control. As such, this isn't the right place to get help. You may want to consider using Spring Boot's ReactiveWebServer build on top of Reactor Netty. Alternatively, the GraalVM community may be able to help.