Issue
I'm setting up a Kafka consumer configuration and the configuration cannot find the keystore or truststore on the classpath:
@EnableKafka
@Configuration
public class KafkaConfig {
@Value("${kafka.ssl.keystore}")
private String keyStorePath;
@Value("${kafka.ssl.truststore}")
private String trustStorePath;
@Bean
public ConsumerFactory<String, String> getConsumerFactory() {
Map<String, Object> properties = new HashMap<>();
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"my-bootstrap.mydomain.com:443");
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
properties.put(ConsumerConfig.GROUP_ID_CONFIG, "group1");
properties.put(ConsumerConfig.CLIENT_ID_CONFIG, "client1");
properties.put("enable.auto.commit", "true");
properties.put("auto.commit.interval.ms", "500");
properties.put("session.timeout.ms", "30000");
properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SSL");
properties.put(SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG, keyStorePath);
properties.put(SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG, "password");
properties.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, trustStorePath);
properties.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG, "password");
properties.put(SslConfigs.SSL_KEY_PASSWORD_CONFIG, "password");
return new DefaultKafkaConsumerFactory<>(properties);
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory
= new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(getConsumerFactory());
return factory;
}
}
The keystore and truststore are both located in the directory src/main/resources/ssl
in the same maven module as the configuration class.
I set up the placeholders in the application.yml as follows:
kafka:
ssl:
keystore: classpath:ssl/kafka-keystore.jks
truststore: classpath:ssl/kafka-truststore.jks
However, the application fails to start with the following exception:
"org.apache.kafka.common.KafkaException: java.io.FileNotFoundException: classpath:ssl/kafka-keystore.jks (No such file or directory)"
My understanding is that using @Value
enables the use of the classpath:
prefix to resolve the classpath (see this link)
https://www.baeldung.com/spring-classpath-file-access
Moreover, the @Value
technique works just fine to resolve the keystores and truststores for the reactive WebClient configuration in the same application.
What do I need to do to resolve the classpath for the Kafka configuration? Am I missing something here?
Solution
Your injecting into a String which is going to keep the "classpath:" within the String value and provide it as a property to DefaultKafkaConsumerFactory, try injecting into a spring Resource like:
import org.springframework.core.io.Resource;
@Value("classpath:path/to/file/in/classpath")
Resource resourceFile;
Then you can access the file and you could get the absolute path like:
resourceFile.getFile().getAbsolutePath()
The idea being you could provide the absolute path to DefaultKafkaConsumerFactory
But you could also try removing the "classpath:" and inject as String like your current code which might work depending on how DefaultKafkaConsumerFactory treats that property. But I can't see why absolute path above wouldn't work.
Answered By - camtastic
Answer Checked By - Gilberto Lyons (JavaFixing Admin)