Native compilation runtime error: java.lang.ClassNotFoundException: org.apache.kafka.common.security.scram.internals.ScramSaslClient$ScramSaslClientFactory
Version of the framework SpringBoot 3.2.7 SpringCloud 2023.0.2 (BOM) Spring Cloud Stream: 4.1.2 GraalVM: CE 21.0.2+13.1 (build 21.0.2+13-jvmci-23.1-b30)
Describe the issue I've got a multi module maven project based on SpingBoot 3.2.x with Spring Cloud Stream 4.1.2 with Kafka Binder (as per Spring Cloud BOM 2023.0.2. I've got a reactive consumer that runs without issue on the JVM. Doing native compiling after the agent has runned for a few minutes, produces a runtime error regarding SASL security mechanism to authenticate to our Kafka Broker.
First af all, here it is our application.yml conifg:
cloud:
refresh:
enabled: false
client:
refresh-enabled: false
config:
default:
logger-level: full
stream:
bindings:
kafkaMessageConsumer-in-0:
destination: devicemanager-topic
kafka:
bindings:
kafkaMessageConsumer-in-0:
consumer:
configuration:
value.deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
spring.json.value.default.type: com.minsait.phygital.resource.devicemanager.outadapter.domain.MetricsMessageBean
binder:
brokers: kafka-test.iiot.ourbroker.com:9098
configuration:
commit.interval.ms: 100
default:
value.deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
spring.json.value.default.type: com.minsait.phygital.resource.devicemanager.outadapter.domain.MetricsMessageBean
security:
protocol: SASL_SSL
sasl:
mechanism: SCRAM-SHA-512
jaas:
config: org.apache.kafka.common.security.scram.ScramLoginModule required username=test password=test-secret;
ssl:
truststore:
location: /path/to/truststore.pem
type: PEM
endpoint:
identification:
algorithm: ""
I will omit consumer code as it has no issue in normal JRE run. I will omit also generated json descriptors produced by GraalVM native-image agent.
The runtime exception launching the executable is:
2024-07-01T16:29:13.266+02:00 WARN 10588 --- [| adminclient-1] org.apache.kafka.clients.NetworkClient : [AdminClient clientId=adminclient-1] Error connecting to node kafka-test.iiot.ourbroker.com:9098 (id: -1 rack: null)
java.io.IOException: Channel could not be created for socket java.nio.channels.SocketChannel[closed]
at org.apache.kafka.common.network.Selector.buildAndAttachKafkaChannel(Selector.java:348) ~[na:na]
at org.apache.kafka.common.network.Selector.registerChannel(Selector.java:329) ~[na:na]
at org.apache.kafka.common.network.Selector.connect(Selector.java:256) ~[na:na]
at org.apache.kafka.clients.NetworkClient.initiateConnect(NetworkClient.java:1032) ~[na:na]
at org.apache.kafka.clients.NetworkClient.ready(NetworkClient.java:301) ~[na:na]
at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.sendEligibleCalls(KafkaAdminClient.java:1109) ~[na:na]
at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.processRequests(KafkaAdminClient.java:1369) ~[na:na]
at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.run(KafkaAdminClient.java:1312) ~[na:na]
at [email protected]/java.lang.Thread.runWith(Thread.java:1596) ~[device-manager:na]
at [email protected]/java.lang.Thread.run(Thread.java:1583) ~[device-manager:na]
at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:833) ~[device-manager:na]
at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211) ~[na:na]
Caused by: org.apache.kafka.common.KafkaException: org.apache.kafka.common.errors.SaslAuthenticationException: Failed to configure SaslClientAuthenticator
at org.apache.kafka.common.network.SaslChannelBuilder.buildChannel(SaslChannelBuilder.java:239) ~[na:na]
at org.apache.kafka.common.network.Selector.buildAndAttachKafkaChannel(Selector.java:338) ~[na:na]
... 11 common frames omitted
Caused by: org.apache.kafka.common.errors.SaslAuthenticationException: Failed to configure SaslClientAuthenticator
Caused by: org.apache.kafka.common.errors.SaslAuthenticationException: Failed to create SaslClient with mechanism SCRAM-SHA-512
Caused by: javax.security.sasl.SaslException: Cannot instantiate service SASL/SCRAM Client Provider: SaslClientFactory.SCRAM-SHA-512 -> org.apache.kafka.common.security.scram.internals.ScramSaslClient$ScramSaslClientFactory
at [email protected]/javax.security.sasl.Sasl.loadFactory(Sasl.java:461) ~[na:na]
at [email protected]/javax.security.sasl.Sasl.createSaslClient(Sasl.java:432) ~[na:na]
at org.apache.kafka.common.security.authenticator.SaslClientAuthenticator.lambda$createSaslClient$0(SaslClientAuthenticator.java:219) ~[na:na]
at [email protected]/java.security.AccessController.executePrivileged(AccessController.java:114) ~[na:na]
at [email protected]/java.security.AccessController.doPrivileged(AccessController.java:714) ~[na:na]
at [email protected]/javax.security.auth.Subject.doAs(Subject.java:525) ~[device-manager:na]
at org.apache.kafka.common.security.authenticator.SaslClientAuthenticator.createSaslClient(SaslClientAuthenticator.java:215) ~[na:na]
at org.apache.kafka.common.security.authenticator.SaslClientAuthenticator.<init>(SaslClientAuthenticator.java:206) ~[na:na]
at org.apache.kafka.common.network.SaslChannelBuilder.buildClientAuthenticator(SaslChannelBuilder.java:285) ~[na:na]
at org.apache.kafka.common.network.SaslChannelBuilder.lambda$buildChannel$1(SaslChannelBuilder.java:228) ~[na:na]
at org.apache.kafka.common.network.KafkaChannel.<init>(KafkaChannel.java:143) ~[na:na]
at org.apache.kafka.common.network.SaslChannelBuilder.buildChannel(SaslChannelBuilder.java:236) ~[na:na]
at org.apache.kafka.common.network.Selector.buildAndAttachKafkaChannel(Selector.java:338) ~[na:na]
at org.apache.kafka.common.network.Selector.registerChannel(Selector.java:329) ~[na:na]
at org.apache.kafka.common.network.Selector.connect(Selector.java:256) ~[na:na]
at org.apache.kafka.clients.NetworkClient.initiateConnect(NetworkClient.java:1032) ~[na:na]
at org.apache.kafka.clients.NetworkClient.ready(NetworkClient.java:301) ~[na:na]
at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.sendEligibleCalls(KafkaAdminClient.java:1109) ~[na:na]
at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.processRequests(KafkaAdminClient.java:1369) ~[na:na]
at org.apache.kafka.clients.admin.KafkaAdminClient$AdminClientRunnable.run(KafkaAdminClient.java:1312) ~[na:na]
at [email protected]/java.lang.Thread.runWith(Thread.java:1596) ~[device-manager:na]
at [email protected]/java.lang.Thread.run(Thread.java:1583) ~[device-manager:na]
at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:833) ~[device-manager:na]
at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211) ~[na:na]
Caused by: java.security.NoSuchAlgorithmException: class configured for SaslClientFactory (provider: SASL/SCRAM Client Provider) cannot be found.
at [email protected]/java.security.Provider$Service.getImplClass(Provider.java:2004) ~[device-manager:na]
at [email protected]/java.security.Provider$Service.getDefaultConstructor(Provider.java:2020) ~[device-manager:na]
at [email protected]/java.security.Provider$Service.newInstanceOf(Provider.java:1934) ~[device-manager:na]
at [email protected]/java.security.Provider$Service.newInstanceUtil(Provider.java:1942) ~[device-manager:na]
at [email protected]/java.security.Provider$Service.newInstance(Provider.java:1917) ~[device-manager:na]
at [email protected]/javax.security.sasl.Sasl.loadFactory(Sasl.java:459) ~[na:na]
... 23 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.apache.kafka.common.security.scram.internals.ScramSaslClient$ScramSaslClientFactory
at [email protected]/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:52) ~[device-manager:na]
at [email protected]/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
at [email protected]/java.lang.ClassLoader.loadClass(ClassLoader.java:121) ~[device-manager:na]
at [email protected]/java.security.Provider$Service.getImplClass(Provider.java:1991) ~[device-manager:na]
... 28 common frames omitted
Please let us know if more infos needed.
@spulci It is likely that we are missing some native runtime hints. Since the offending class (ScramSaslClient$ScramSaslClientFactory) is part of Apache Kafka and not maintained by Spring for Apache Kafka project, we would ideally like to see this runtime hint added in the Oracle's graalvm reachability metadata repository here. Can you try adding a native hint locally in your project and see if this resolves? If so, please consider sending a PR to the repository I mentioned.
@spulci It is likely that we are missing some native runtime hints. Since the offending class (
ScramSaslClient$ScramSaslClientFactory) is part of Apache Kafka and not maintained by Spring for Apache Kafka project, we would ideally like to see this runtime hint added in the Oracle's graalvm reachability metadata repository here. Can you try adding a native hint locally in your project and see if this resolves? If so, please consider sending a PR to the repository I mentioned.
@sobychacko thanks Soby and accept my apologize due to the fact the the issue is not related to Spring Cloud Stream. Taking care of the fact I'm a real newbie in Java Native compilation, can you help me to elaborate the missing descriptor? I've tried this one;
{
"name": "org.apache.kafka.common.security.scram.internals.ScramSaslClient$ScramSaslClientFactory",
"queryAllPublicMethods": true,
"queryAllPublicConstructors": true,
"condition": {
"typeReachable": "javax.security.sasl.SaslClientFactory"
},
"methods": [
{
"name": "<init>",
"parameterTypes": [
]
}
]
}
added to my resource/META-INF/native-image/
Can you try to add a runtime hints class in your application like this one? Then provide a corresponding aot.factories file. Start with that and see if it helps. If it doesn't, please share a sample application so we can investigate it further.
Ok,
issue has been fixed locally with these two added hints:
hints.reflection().registerType(
org.apache.kafka.common.security.scram.internals.ScramSaslClient.ScramSaslClientFactory.class,
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
hints.reflection().registerType(org.apache.kafka.common.security.scram.ScramLoginModule.class,
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
Related metadata in reflect-config.json is:
{
"name": "org.apache.kafka.common.security.scram.internals.ScramSaslClient$ScramSaslClientFactory",
"allDeclaredConstructors": true,
"allDeclaredMethods": true
},
{
"name": "org.apache.kafka.common.security.scram.ScramLoginModule",
"allDeclaredConstructors": true,
"allDeclaredMethods": true
}
Please note that I've added ScramLoginModule too otherwise the app would have crashed on this class too (used in my spring cloud stream).
I will try to open a PR to the other repo soon, citing this issue too. Please feel free to track that PR when opened and of course suggest me any improvement I can do in this configuration.
Many thanks for your help!
Simone
I'm glad to hear that it worked. Yes, please send a PR to the other repo. You should probably still keep your local hints until we have a release from the reachability metadata and it is incorporated with the spring native bits. Here is a recent PR I sent to that repo if you need an example: https://github.com/oracle/graalvm-reachability-metadata/pull/422.
Also, can we close this issue?
Thanks!
I'm closing the issue. Hope to have the PR tomorrow.