I am trying to retrieve value from application.yml. The final line below is showing kafkaConfig as null, and cannot read. How do I setup the Kafka Config and code properly, to read from the json file? We are using #Data instead of getters/setters.
KafkaConfig.java
#Configuration
#ConfigurationProperties("kafka")
#Data
public class KafkaConfig {
private String topic;
private String event;
}
Application.yml
kafka:
topic: "testTopic"
event: "testEvent"
KafkaProducerBeans.java
#Component
public class KafkaProducerBeans {
#Autowired
private KafkaConfig kafkaConfig;
public KafkaProducerBeans(KafkaConfig kafkaConfig) {
this.kafkaConfig = kafkaConfig;
}
#Bean(name = "kafkaTestClient")
public String getData() {
return kafkaConfig.getTopic(); // final line is creating null for kafka Config
}
Resource: https://codingnconcepts.com/spring-boot/spring-configuration-properties-using-yml/
I think the recommended way of binding properties to pojos is to use the #EnableConfigurationProperties annotation like so:
KafkaConfig.java
#ConfigurationProperties("kafka")
#Data
public class KafkaConfig {
private String topic;
private String event;
}
KafkaProducerBeans.java
#Component
#EnableConfigurationProperties(KafkaConfig.class)
public class KafkaProducerBeans {
private final KafkaConfig kafkaConfig;
#Autowired
public KafkaProducerBeans(KafkaConfig kafkaConfig) {
this.kafkaConfig = kafkaConfig;
}
// [...]
}
Refer to the official Spring Documentation for further details:
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.typesafe-configuration-properties.java-bean-binding
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.typesafe-configuration-properties.enabling-annotated-types
Add one more annotation #EnableConfigurationProperties on the class KafkaConfig
KafkaConfig.java
#Configuration
#EnableConfigurationProperties // new added annotation
#ConfigurationProperties("kafka")
#Data
public class KafkaConfig {
private String topic;
private String event;
}
#ComponentScan(basePackages ="PATH" )
PATH: the package path you want it to look for
#ComponentScan(basePackages ="PATH" )
#Configuration
#Import(value = {
KafkaProducerBeans.class
})
public class AppConfig{
}
Related
I have the following yaml-property file:
myPrefix:
value: Hello
myPrefix2:
value: World
And two classes
#PropertySource("classpath:permission-config.yml")
#ConfigurationProperties(prefix = "myPrefix")
#Component
#Getter
#Setter
public class ViewUsers {
private String value;
}
and
#PropertySource("classpath:permission-config.yml")
#ConfigurationProperties(prefix = "myPrefix2")
#Component
#Getter
#Setter
public class ManageUsers {
private String value;
}
Then null is injected.
Or, if I try to use #Value then ONLY latest value is retrieved, which is the last one (World), the preceding ones are always ignored.
Try the following approach:
Remove #Component from configuration properties (ViewUsers and ManageUsers)
Instead, use the following construction:
#PropertySource("classpath:permission-config.yml")
#ConfigurationProperties(prefix = "myPrefix")
public class ViewUsers {
private String value; // getter, setter
}
#PropertySource("classpath:permission-config.yml")
#ConfigurationProperties(prefix = "myPrefix2")
public class ManageUsers {
private String value; // getter, setter
}
#Configuration
#EnableConfigurationProperties({ViewUsers.class, ManageUsers.class}
public class MySampleConfiguration {
... beans here...
}
Also make sure that Lombok annotations are working as expected (Try to use without lombok just for the POC).
Update 1
As #M. Deinum has kindly stated, PropertySource like this doesn't work with yaml files.
You can try the following workaround:
#Configuration
#PropertySource(name = "someName", value = "classpath:foo.yaml", factory = YamlPropertySourceFactory.class)
public class MyConfig {
}
import org.springframework.boot.env.YamlPropertySourceLoader;
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
final List<PropertySource<?>> load = new YamlPropertySourceLoader().load(name, resource.getResource());
return load.get(0);
}
}
Currently I have a repositories package. I map that package to the MongoTemplate like this
#Setter
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "myservice.mongodb")
#EnableMongoRepositories(basePackages = { "mycustom.repository" }, mongoTemplateRef = "customMongoTemplate")
public class CustomMongoConfiguration extends MongoConfiguration {
private String host;
private String database;
private int port;
#Bean(name = "customMongoTemplate")
public MongoTemplate getMongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory(host,port,database));
}
}
In mongoDbFactory(), I'm using SimpleMongoDbFactory() to return an instance of MongoDbFactory.
Is there a way to make the mapping of my mongoTemplate bean to my repository package during runtime/dynamically?
A small example would be helpful. Thanks
As the title says, I'm trying to use Typesafe Configuration Properties to load a list of DataSourceConfig objects. I have lombok for setter/getters
The main application class annotations
#Slf4j
#SpringBootApplication
#EnableConfigurationProperties
public class Application {
The configuration pojo
#Data
public class DataSourceConfig {
private String key;
private String dbname;
private String dbpath;
}
The yml file
tenantdb:
dataSourceConfig:
-
key: default
dbpath: file:eventstore/jdbc/database
dbname: defaultdb
-
key: other
dbpath: file:eventstore/jdbc/other
dbname: dslfjsdf
Finally, the Spring Configuration class with the #ConfigurationProperties annotation.
#Configuration
#Profile("hsqldb")
#ImportResource(value = { "persistence-config.xml" })
#Slf4j
#ConfigurationProperties(prefix="tenantdb", locations={"datasources.yml"})
public class HsqlConfiguration {
private List<DataSourceConfig> dataSourceConfig = new ArrayList<>();
#Bean
public List<DataSourceConfig> getDataSourceConfig() {
return dataSourceConfig;
}
With the config above, I get:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hsqlConfiguration': Could not bind properties to [unknown] (target=tenantdb, ignoreInvalidFields=false, ignoreUnknownFields=true, ignoreNestedProperties=false); nested exception is java.lang.NullPointerException
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:303)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:250)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initia
I've tried various combinations. If I change the annotation to #ConfigurationProperties(prefix="tenantdb.dataSourceConfig"), I don't get the error but List<DataSourceConfig> is empty.
HELP!!
You should use configuration properties as simple POJO with only getters and setters and have separate HsqlConfiguration which has this properties injected.
Something like this:
#Component
#ConfigurationProperties(prefix="tenantdb", locations={"datasources.yml"})
public class TenantDbProperties {
//DataSourceConfig is POJO with key, dbpath and dbname
private List<DataSourceConfig> dataSourceConfigs;
public List<DataSourceConfig> getDataSourceConfigs(){
return dataSourceConfigs;
}
public void setDataSourceConfigs(List<DataSourceConfig> dataSourceConfigs){
this.dataSourceConfigs = dataSourceConfigs;
}
}
And in separate class have this properties injected as:
#Configuration
#Profile("hsqldb")
#ImportResource(value = { "persistence-config.xml" })
#Slf4j
public class HsqlConfiguration {
#Autowired
private TenantDbProperties tenantDbProperties;
//code goes here where you can use tenantDbProperties.getDataSourceConfigs()
}
I am trying to get a Spring-boot application going and I am not sure what I am doing wrong here. I have a application.properties file at src/main/resources & src/test/resources. I have an #Bean for my ConfigurationSettings so that I can use them throughout my application:
#Component
public class ConfigurationSettings {
private String product;
private String version;
private String copyright;
private String appName;
private String appDescription;
...
// getters and setters
}
Here is how I kick the application off:
#Configuration
#EnableJpaRepositories
#EnableAutoConfiguration
#EnableConfigurationProperties
#PropertySources(value = {#PropertySource("classpath:application.properties")})
#ComponentScan(basePackages = "com.product")
#EnableScheduling
public class OFAC {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run( OFAC.class, args );
}
And here is my configuration class:
#Configuration
#ComponentScan(basePackages = {"com.product"})
#PropertySources(value = {#PropertySource("classpath:application.properties")})
public class OFAConfiguration {
#Autowired
private Environment env;
#Bean
public ConfigurationSettings configurationSettings() {
ConfigurationSettings configurationSettings = new ConfigurationSettings();
configurationSettings.setAppDescription( env.getRequiredProperty("app.description" ) );
configurationSettings.setAppName( env.getRequiredProperty( "app.name" ) );
configurationSettings.setServerPort( env.getRequiredProperty( "server.port" ) );
return configurationSettings;
}
I am trying to use it in a controller:
#RestController
public class AboutController {
#Autowired
private ConfigurationSettings configurationSettings;
#RequestMapping(value = "/about", method = RequestMethod.GET)
public About index() {
String product = configurationSettings.getProduct();
String version = configurationSettings.getVersion();
String copyright = configurationSettings.getCopyright();
return new About( product, version, copyright );
}
}
However, when step thru this, all the values of ConfigurationSettings are null. I do have a test that successfully loads the values:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {OFAConfiguration.class})
public class OFAConfigurationTest {
#Autowired
private Environment environment;
#Autowired
private ConfigurationSettings configurationSettings;
#Test
public void testConfigurationLoads() {
assertNotNull(environment);
Assert.assertNotNull(configurationSettings);
}
#Test
public void testConfigurationSettingValues() {
assertEquals("Product Name", configurationSettings.getProduct());
assertEquals("0.0.1", configurationSettings.getVersion());
assertEquals("2014 Product", configurationSettings.getCopyright());
}
Can anyone see why the ConfigurationSettings are not being populated in my Controller?
Your configuration leads to 2 instances of the ConfigurationSettings class and probably one instance overrides the other.
The 'ConfigurationSettings' has the #Component annotation as you are scanning for components (#ComponentScan) this will lead to an instance. You also have a #Bean annotated method which also leads to an instance. The latter is overridden with the first.
In short remove the #Component annotation as that isn't needed because you already have a factory method for this class.
public class ConfigurationSettings { ... }
You should also remove the #PropertySource annotations as Spring-Boot will already load the application.properties for you.
Finally you should not use the #ContextConfiguration annotation on your test class but the #SpringApplicationConfiguration and pass in your application class (not your configuration class!).
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes=OFAC.class)
public class OFAConfigurationTest {
#Autowired
private Environment environment;
#Autowired
private ConfigurationSettings configurationSettings;
#Test
public void testConfigurationLoads() {
assertNotNull(environment);
assertNotNull(configurationSettings);
}
#Test
public void testConfigurationSettingValues() {
assertEquals("Product Name", configurationSettings.getProduct());
assertEquals("0.0.1", configurationSettings.getVersion());
assertEquals("2014 Product", configurationSettings.getCopyright());
}
This will fix your runtime configuration problems and will let your test use the power of Spring Boot to configure your application.
In my spring boot application, I have following configuration:
server:
host: a.com
port: 5922
enable-log: true
I want to read the above as a java.util.Properties. I tried putting following class:
#ConfigurationProperties(prefix = "server")
public class ServerConfig {
private Properties serverProps;
// ... Getter/setter
}
The boot config files look like:
#Configuration
#ComponentScan("com.demo")
#EnableAspectJAutoProxy(proxyTargetClass = true)
#EnableConfigurationProperties({ServerConfig.class})
#Profile({"dev"})
public class TestAppConfiguration {
}
#EnableAutoConfiguration
#SpringBootApplication
public class TestAppInitializer {
public static void main(final String[] args) {
SpringApplication.run(TestAppInitializer.class, args);
}
}
Unit test class:
#SpringApplicationConfiguration(classes = {TestAppInitializer.class})
#ActiveProfiles("dev")
public class ServerConfigTest extends AbstractTestNGSpringContextTests {
#Autowired
private ServerConfig serverConfig;
#Test
public void printDetails() {
logger.debug("ServerConfig.properties --> {}", serverConfig.getProperties());
}
}
The output is
ServerConfig.properties --> null.
I am not sure why is this happening. The only idea that comes to my mind is, the configuration parameters underneath server are basically flat strings. Is there any way I can read those as Properties?
#Bean({"kaptchaProperties"})
#ConfigurationProperties(prefix = "custom.kaptcha")
public Properties getProperties() {
return new Properties();
}
AFAIK, you can't load them into java.util.Properties with annotation #ConfigurationProperties.
You need to create this:
#Component
#ConfigurationProperties(prefix = "server")
public class ServerConfig {
private String host;
private String port;
private String enableLog;
// ... Getter/setter
}