3

I'm working with a Spring application that has configurations defined for every year in an application.properties file. Basically at this point it's a copy-paste job, just incrementing the pasted configuration by 1. We have an analogous copy-paste process in several classes with bean definitions for every year as well.

Because there's a requirement to be able to access things defined by previous years' configurations, I'm thinking it would be simpler to provide a property for the current year, then define all the beans with some sort of parameterized properties. I'm not finding a great way to do this, though. Currently we have:

application.properties:

year2020.datasource.jdbcUrl=jdbc:postgresql://${built.url.2020}:5432/db_name
year2020.datasource.username=some_username
year2020.datasource.password=some_password
year2020.datasource.driverClassName=org.postgresql.Driver
year2020.datasource.validationQuery=SELECT 1

# And multiple similar configs for other resources and previous years

And some classes for the beans:

@Configuration
public class CopyPastedDataSource {

    @Bean(name = "year2020_datasource")
    @ConfigurationProperties(prefix = "year2020.datasource")
    public DataSource dataSource2020() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "year2019_datasource")
    @ConfigurationProperties(prefix = "year2019.datasource")
    public DataSource dataSource2020() {
        return DataSourceBuilder.create().build();
    }

    // And more beans for previous years
}

I was thinking maybe just replacing the year with a placeholder and replacing it with a year as needed at runtime. Is there a way to achieve this that would be more idiomatic or maintainable?

Aweston
  • 379
  • 2
  • 10
  • 1
    Why define multiple DataSource beans? Why not have just one, and parameterize that one definition with the configuration for whatever year you're interested in. Furthermore, there's no reason to break out all of the parameters for a year into properties. It would be easier and more appropriate to collect those somewhere else, like in individual text files, or in a database. You could have JSON files named by year, and read and parse the JSON for whatever year you've specified via a single property, and then use the data in the data structure from the JSON file to set up the DataSource. – CryptoFool Sep 29 '20 at 17:07
  • It's giving me a headache just looking at that. Does the app actually use the 2020 datasource along with the 2019 one? How do you handle injecting the write one into a consumer bean? – Robert Moskal Sep 29 '20 at 17:45
  • ¯\\_(ツ)_/¯ That's what I'm trying to work out. The user needs to be able to access data sources from previous years. We realize 100% that this is ridiculous, but that's the way it was originally set up and we're stuck with it for now (read: probably forever bc this happens "only once a year"). – Aweston Sep 29 '20 at 20:04
  • +1 to the solution of just having 1 bean and give it parameters, then you could just get the current year and do it programmatically. The trouble I see there, is you really need to save the config for previous years? If that's the case I don't know what could be done, as the alternative would be some kind of Constant-Wrapper class, but a properties file is better for this kind of config. – Mario Codes Sep 30 '20 at 10:38

1 Answers1

1

So it turns out I was looking for exactly this approach:

https://stackoverflow.com/a/26276872/7660079

https://stackoverflow.com/a/53554102/7660079

Instead of autowiring individual resources one by one and then collecting them into a single bean, you can autowire properties into a Map pretty easily. Here's an example from our list of similarly numbered API endpoints:

application.properties

api.url.2020=api-2020.endpoint.com
api.url.2019=api-2019.endpoint.com
api.url.2018=api-2018.endpoint.com

Bean:

@Configuration
@EnableConfigurationProperties

public class ApiConfiguration {
    @Bean
    @ConfigurationProperties(prefix = "api.url")
    public Map<Integer, String> getConfiguration() {
        return new HashMap<>();
    }
}

Autowired bean:

@Component
class APIConsumer {
    @Autowired
    Map<Integer, String> apiUrlsByYear;
}
Aweston
  • 379
  • 2
  • 10