Search code examples
javaapache-kafkajbosswildflykeycloak

Kafka Java Client: ClassLoader doesn't find SASL/Scram Login Class


Building an SPI to push events into Kafka, to get deployed as an EAR inside Keycloak 6.0.1, which uses the WildFly Server, packaged in a Docker image based on jboss/keycloak:6.0.1.

I ran into: Kafka Producer - org.apache.kafka.common.serialization.StringSerializer could not be found

So I applied the suggested solution of setting Thread.currentThread().setContextClassLoader(null);.

This seems to work fine for my local Kafka on port 9092, without authentication. As soon as I authenticate as described here:

String jaasTemplate = "org.apache.kafka.common.security.scram.ScramLoginModule required username=\"%s\" password=\"%s\";";
String jaasCfg = String.format(jaasTemplate, username, password);

Properties props = new Properties();
props.put("sasl.jaas.config", jaasCfg);
// ...
Thread.currentThread().setContextClassLoader(null);
KafkaProducer<String, String> producer = new KafkaProducer<>(props);

I run into the error:

org.apache.kafka.common.KafkaException: javax.security.auth.login.LoginException: unable to find LoginModule class: org.apache.kafka.common.security.scram.ScramLoginModule
    at org.apache.kafka.common.network.SaslChannelBuilder.configure(SaslChannelBuilder.java:160)
    at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:146)
    at org.apache.kafka.common.network.ChannelBuilders.clientChannelBuilder(ChannelBuilders.java:67)
    at org.apache.kafka.clients.ClientUtils.createChannelBuilder(ClientUtils.java:99)
    at org.apache.kafka.clients.producer.KafkaProducer.newSender(KafkaProducer.java:441)
    at org.apache.kafka.clients.producer.KafkaProducer.<init>(KafkaProducer.java:422)

I suspect that this is caused by setting the ClassLoader to null, but I'm not certain.

The JAAS string mentions this org.apache.kafka.common.security.scram.ScramLoginModule. I tried not using JAAS but instead plain username + password like so:

Properties props = new Properties();
props.put("sasl.username", username);
props.put("sasl.password", password);
// ...
Thread.currentThread().setContextClassLoader(null);
KafkaProducer<String, String> producer = new KafkaProducer<>(props);

But that also resulted in an Exception, just mentioning a different Class could not be located:

org.apache.kafka.common.KafkaException: javax.security.auth.login.LoginException: unable to find LoginModule class: org.jboss.as.security.remoting.RemotingLoginModule
    at org.apache.kafka.common.network.SaslChannelBuilder.configure(SaslChannelBuilder.java:160)
    at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:146)
    at org.apache.kafka.common.network.ChannelBuilders.clientChannelBuilder(ChannelBuilders.java:67)
    at org.apache.kafka.clients.ClientUtils.createChannelBuilder(ClientUtils.java:99)
    at org.apache.kafka.clients.producer.KafkaProducer.newSender(KafkaProducer.java:441)
    at org.apache.kafka.clients.producer.KafkaProducer.<init>(KafkaProducer.java:422)

How can I make it, that the org.apache.kafka.common.security.scram.ScramLoginModule is found? Thanks!


Solution

  • Warning: ugly hack!

    Just before you create the KafkaProducer, set the context classloader shown bellow:

    Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());