Niranjan
Niranjan

Reputation: 2921

How to load multiple properties from YML file in spring-boot application

We have a YML configuration which looks like:

datasurces: 
  readDataSource:
    ssl-enabled: false
    driver-class-name: oracle.jdbc.driver.OracleDriver
    host: db1.abc.com
    port: 1232
    sid: ABC_DB
    trust-store-fileName: abcdb.jks
    connection-pool:
      initial-size: 10
      max-size: 20

  writeDataSource:
    ssl-enabled: false
    driver-class-name: oracle.jdbc.driver.OracleDriver
    host: db2.abc.com
    port: 1232
    sid: XYZ_DB
    trust-store-fileName: xyzdb.jks
    connection-pool:
      initial-size: 10
      max-size: 20

We have to load this to a custom class DataSources which looks like

@ConfigurationProperties(prefix = "datasources")
public class DataSources {
  @Value("${datasources.readDataSource}")
  private DataSource readDataSource;

  @Value("${datasources.writeDataSource}")
  private DataSource writeDataSource;

  //...getters/setters
}

public class DataSource {
  private String id;
  private boolean sslEnabled;
  private String driverClassName;
  private String host;
  private int port;
  private String trustStoreFileName;
  private ConnectionPool connectionPool;

  //...getters/setters  
}

public class ConnectionPool {
  private int initialSize;
  private int maxSize;

  //...getters/setters 
}

My configuration files for spring boot looks like:

@Configuration
@ComponentScan(basePackages = {"com.abc"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableConfigurationProperties(DataSources.class)
@Profile({"dev"})
public class TestAppConfiguration {

}

@EnableAutoConfiguration
@SpringBootApplication
@EnableConfigurationProperties(DataSources.class)
public class TestAppInitializer {
  @Autowired
  private DataSources dataSources;

  public static void main(final String[] args) {
    SpringApplication.run(TestAppInitializer.class, args);
  }
}

The unit test is:

@SpringApplicationConfiguration(classes = {TestAppInitializer.class})
@Test(groups = "categoryTests")
@ActiveProfiles("dev")
public class DataSourcesTest extends AbstractTestNGSpringContextTests {
  private static final AppLogger logger = LoggerUtil.getLogger(DataSourcesTest.class);

  @Autowired
  private DataSources dataSources;

  @Test
  public void printDetails() {
    logger.debug("DataSources --> {}", dataSources);
  }
}

Result is not as expected.

  1. When I remove @Value from DataSources class, both the properties readDataSource and writeDataSource are null (The DataSources class itself is not null).
  2. When I add @Value in DataSources class, the test fails with exception

    Caused by: java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.abc.DataSource]: no matching editors or conversion strategy found

Appreciate if someone can provide some idea how to deal with this. All I want is to capture both readDataSource and writeDataSource inside a class like DataSources.

Upvotes: 0

Views: 6100

Answers (1)

M. Deinum
M. Deinum

Reputation: 124431

Annotate your DataSources class with @Configuration then create 2 @Bean methods annotatated with @ConfigurationProperties.

@Configuration
public class DataSources {

    @Bean
    @ConfigurationProperties(prefix="datasources.readDataSource")
    public DataSource readDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix="datasources.writeDataSource")
    public DataSource writeDataSource() {
        return DataSourceBuilder.create().build();
    }
}

Now you have 2 datasources with the properties bound to the created DataSources. This mechanism is explained here in the Spring Boot Reference Guide.

The same would apply if you don't need a DataSource but construct your own object (although not sure why you would need that?).

Upvotes: 6

Related Questions