I was looking for loading keystore/truststore through classpath and here is one of the first links I got. I was able to find a solution and since I was using Spring Boot and Spring Kafka - configuration only with properties - I was looking for a solution like this, so this answer might help other people as well. So, there's a BeanPostProcessor class that you can implement. It provides two methods:
postProcessAfterInitialization and postProcessBeforeInitialization
Factory hook that allows for custom modification of new bean instances — for example, checking for marker interfaces or wrapping beans with proxies. Typically, post-processors that populate beans via marker interfaces or the like will implement postProcessBeforeInitialization(java.lang.Object, java.lang.String), while post-processors that wrap beans with proxies will normally implement postProcessAfterInitialization(java.lang.Object, java.lang.String).
I was using Spring Boot with Spring Kafka and I only wanted a change for local profile.
In my code example, I was using it to override Kafka Location properties, because for SSL it doesn't read from classpath.
So this was the code:
import io.confluent.kafka.schemaregistry.client.SchemaRegistryClientConfig; import java.io.IOException; import java.util.Arrays; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.apache.kafka.common.config.SslConfigs; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.kafka.KafkaProperties; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; @Configuration @RequiredArgsConstructor public class KafkaConfiguration implements BeanPostProcessor { @Value("${spring.kafka.ssl.key-store-location:}") private Resource keyStoreResource; @Value("${spring.kafka.properties.schema.registry.ssl.truststore.location:}") private Resource trustStoreResource; private final Environment environment; @SneakyThrows @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof KafkaProperties) { KafkaProperties kafkaProperties = (KafkaProperties) bean; if(isLocalProfileActive()) { configureStoreLocation(kafkaProperties); } } return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } private boolean isLocalProfileActive() { return Arrays.stream(environment.getActiveProfiles()).anyMatch(profile -> "local".equals(profile)); } private void configureStoreLocation(KafkaProperties kafkaProperties) throws IOException { kafkaProperties.getSsl().setKeyStoreLocation(new FileSystemResource(keyStoreResource.getFile().getAbsolutePath())); kafkaProperties.getProperties().put(SchemaRegistryClientConfig.CLIENT_NAMESPACE + SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG, keyStoreResource.getFile().getAbsolutePath()); kafkaProperties.getSsl().setTrustStoreLocation(new FileSystemResource(trustStoreResource.getFile().getAbsolutePath())); kafkaProperties.getProperties().put(SchemaRegistryClientConfig.CLIENT_NAMESPACE + SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, trustStoreResource.getFile().getAbsolutePath()); } }
This way I could have on my properties file:
spring.kafka.ssl.key-store-location=classpath:mykeystore.jks
And the code would get the absolute path from that and set it. It makes also possible to filter based on profiles.
It's important to mention that BeanPostProcessor runs for EVERY bean, so make sure you filter what you want.