5

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!!

Raghu
  • 1,140
  • 1
  • 14
  • 22
  • My configuration properties are annotated with `@Component` and it gets filled while component scanning, have you tried that? Also two more things, where is `datasources.yml` located and why is `getDataSourceConfig` annotated as bean? – Nenad Bozic Mar 19 '15 at 09:00
  • `datasources.yml` is at the classpath root. `getDataSourceConfig` is annotated as a bean so that I can inject it elsewhere as well. – Raghu Mar 19 '15 at 09:13
  • 1
    I tried to play around with your code, created test and I get list of 2 `DataSourceConfig` as expected. Only thing is that they are empty (have `null` for `key`, `dbname` and `dbpath`. I provided setters on that class and it binded fine, might be that? – Nenad Bozic Mar 19 '15 at 09:46
  • Can you put `ignoreUnknownFields = false` and check what you get? – Nenad Bozic Mar 19 '15 at 09:48
  • Did you try with `@ConfigurationProperties(prefix="tenantdb.dataSourceConfig")`? I put `ignoreUnknownFields` - no difference in behavior? For the binding behavior - I have lombok - so setters/getters are generated. – Raghu Mar 19 '15 at 10:06
  • Might be some lombok - spring boot collision, I tried with `@ConfigurationProperties(prefix="tenantdb", locations={"datasources.yml"})` and it works as expected, only thing I do not want to add lombok so used regular setter/getters. Also I had problems when I placed yml file in /config location but when I placed it to `src/main/resources` it started working – Nenad Bozic Mar 19 '15 at 10:10
  • What's your version of spring-boot? I'm on 1.2.1.RELEASE. I get an NPE with `@ConfigurationProperties(prefix="tenantdb", locations={"datasources.yml"})` :( - at this location `at org.springframework.boot.bind.RelaxedDataBinder.extendCollectionIfNecessary(RelaxedDataBinder.java:248)` – Raghu Mar 19 '15 at 10:21
  • Can you upload your code someplace? I'm at my wit's end with this :( – Raghu Mar 19 '15 at 10:54
  • 1
    A `@ConfigurationProperties` bean is meant to be a simple pojo. All these annotations you added on it seems like the wrong place to me. Your `@Bean` on `DataSourceConfig` is definitely wrong. Please move only the configuration part to a bean with just `@ConfigurationProperites` on it. Lombok is supported. – Stephane Nicoll Mar 19 '15 at 11:51
  • @StéphaneNicoll Removing the @Bean from `DataSourceConfig` was the problem - getting rid of that fixed it; Now I inject the HsqlConfiguration itself into other places where I need the config and things seem to be working. If you can create an answer, I'll accept it; Also can you let me know which ones specifically look wrong = annotation wise? I probably have to restructure a bit; @Nenad - Thanks for your help as well – Raghu Mar 19 '15 at 12:02
  • 1
    As I said in first comment you should probably make `TenantDbProperties` which are just `@Component` and `@ConfigurationProperties(prefix="tenantdb", locations={"datasources.yml"})` and are simple pojo as @StéphaneNicoll suggested and have `HsqlConfiguration` with other configuration and component scan on package where properties are – Nenad Bozic Mar 19 '15 at 13:40
  • @NenadBozic, make your responses into answers so that we can accept. – ben3000 Nov 17 '15 at 05:04
  • My answer was from March so I will try but correct me if I am wrong since it was while ago – Nenad Bozic Nov 17 '15 at 07:55

1 Answers1

5

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()
}
Nenad Bozic
  • 3,724
  • 19
  • 45