3

Given the following simple spring boot example application:

@SpringBootApplication
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }   
}

and

@PropertySource(value="classpath:properties/tables/adres.properties")
@PropertySource(value="classpath:properties/tables/person.properties")
@Component
public class Sample {

    @Value("${table.name}")
    private String tableName;

    @Value("${table.columns}")
    private String[] columns;

    @Value("${table.data.types}")
    private String[] dataTypes;

    @PostConstruct
    public void init() {
        // do something with values from properties...
        for (String column : columns) {
            System.out.println(column);
        }
    }
}

and the following example properties:
adres.properties:

table.name=adres
table.columns=personId,streetName,cityName
table.data.types=integer,string,string

person.properties:

table.name=person
table.columns=personId,firstName,lastName
table.data.types=integer,string,string

I want to

  1. add all the properties files from a directory using one import instead of having to add each individual property as a @PropertySource
  2. retreive the values for each individual property file and do something with them

I have tried the following:

1.

Using the wildcard * to get all properties from a directory ( http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html#resources-classpath-wildcards ) as follows:

@PropertySource(value="classpath*:properties/tables/*.properties")

throws java.io.FileNotFoundException: class path resource [properties/tables/*.properties] cannot be opened because it does not exist, so it looks like it is parsing the * as a literal value instead of a wildcard.

Adding a PropertySourcesPlaceholderConfigurer to the Application class as suggested in How to read multiple properties having the same keys in Spring?:

@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
    PropertySourcesPlaceholderConfigurer config = new PropertySourcesPlaceholderConfigurer();
    config.setIgnoreUnresolvablePlaceholders(true);
    return config;
}

Doesn't seem to work either because the java.io.FileNotFoundException gets thrown before the PropertySourcesPlaceholderConfigurer is loaded.

2.

Retreiving the values also prove difficult because each individual property uses the same keys. This is done for consistency and maintability of the property files. I have tried solving this by using some more placeholders:

table.name=person
{table.name}.columns=personId,firstName,lastName
{table.name}.data.types=integer,string,string

and in the Sample.class

@Value("${table.name}")
private String tableName;

@Value("${{table.name}.columns}")
private String[] columns;

@Value("${{table.name}.data.types}")
private String[] dataTypes;

The placeholders work, but I still have to manually add all the @PropertySource and can only get the @Value from the @PropertySource that was loaded last.

EDIT: the placeholders actually dont work. When using the the following syntax: ${table.name}.columns=personId,firstName,lastName and @Value("${${table.name}.columns}") the following exception occurs:

Could not resolve placeholder 'person.columns' in string value "${${table.name}.columns}" 

Question

How do I solve my problem with regard to loading multiple property files and then retrieving the values from each individual property file in a java configuration style manner (but still using the same key names) ?

EDIT 2: Partial solution / Workaround

Managed to create a workaround solution with regard to the value clashing:

@PropertySource(value="classpath:properties/tables/tables.properties")
@PropertySource(value="classpath:properties/tables/person.properties")
@PropertySource(value="classpath:properties/tables/address.properties")
@Component
public class Sample {

    private static final String COLUMNS = ".columns";
    private static final String DATA_TYPES = ".data.types";

    @Autowired
    private Environment env;

    @Value("${table.names}")
    private String[] tableNames;    

    @PostConstruct
    public void init() {
        for (String tableName : tableNames) {
            getTableValues(tableName);
        }       
    }

    private void getTableValues(String tableName) {
        String col = env.getProperty(tableName + COLUMNS);
        List<String> columns = Arrays.asList(col.split("\\s*,\\s*"));
        for (String column : columns) {
            System.out.println(String.format("The table %s contains the column %s", tableName, column));
        }
        String types = env.getProperty(tableName + DATA_TYPES);
        List<String> dataTypes = Arrays.asList(types.split("\\s*,\\s*"));
        // do more stuff
    }
}
Community
  • 1
  • 1
Sander_M
  • 1,109
  • 2
  • 18
  • 36
  • 1
    Yes this is very tricky because in general the assumption is that all properties are unique across all files. I am not sure how to do it but I think what you would need to do is create your own property reader that prepends the file name somehow to all properties. Maybe look at this answer http://stackoverflow.com/a/25884743/4768873 and see if you can create your own custom resource which can load from multiple files in a directory and filter properties. – Rob Baily Aug 03 '16 at 13:33
  • I like your idea and it crossed my mind to make one property file with all the names of the other property files (person, addres, etc) and then based on that property file create "hard coded" property files and use springs `Environment` class to loop over them and get the values I want. This just seems like such a long haul for something that seems so trivial.. – Sander_M Aug 03 '16 at 13:42
  • I think what is not trivial is that you want to have the properties the same names. If everything was already name spaced in the files I think it would be much easier. – Rob Baily Aug 04 '16 at 14:04

0 Answers0