spring-cloud-config: Config /refresh not compatible with custom bootstrapped properties
Enhancement
We have a scenario where we called a refresh endpoint, whilst this initially worked fine for our test case, as we layered in some of our custom components (for eureka discovery, security properties etc) we ran into issues where the refresh would fail to be able to locate the properties , that would have been initially injected via @ConfigurationProperties.
After some trawling into the depths, I discovered that the issue was inside ContextRefresher. It uses:
private static final String[] **DEFAULT_PROPERTY_SOURCES** = new String[] {
// order matters, if cli args aren't first, things get messy
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
"defaultProperties" };
And the call to copyEnvironment here:
/* for testing */ ConfigurableApplicationContext addConfigFilesToEnvironment() {
ConfigurableApplicationContext capture = null;
try {
StandardEnvironment environment = **copyEnvironment**(
this.context.getEnvironment());
Causes us to go back around a refresh loop, and the classes that were using @ConfigurationProperties will fail to locate their property sources, as they werent in the items considered to be default.
We have been able to get around this issue, by copying the code from ContextRefresher into our own class and defining a bean, so that it gets called instead.
We only had to change one piece of information to make it work - to extend the default_property_sources to load in our custom ones.
private static final String[] DEFAULT_PROPERTY_SOURCES = new String[] {
// order matters, if cli args aren't first, things get messy
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
"defaultProperties",**"customProperties"** };
However since so much of the code was in private we now have a class that essentially is a copy with a minor change. It would be better if the framework would either allow you to adjust the DEFAULT_PROPERTY_SOURCEs, perhaps by providing a protected method that gets called here instead of the DEFAULT_PROPERTY_SOURCES. By default the method (getDefaultPropertySources() would return the static constant, but would be allowed to be overridden.
// Don't use ConfigurableEnvironment.merge() in case there are clashes with property
// source names
private StandardEnvironment copyEnvironment(ConfigurableEnvironment input) {
StandardEnvironment environment = new StandardEnvironment();
MutablePropertySources capturedPropertySources = environment.getPropertySources();
// Only copy the default property source(s) and the profiles over from the main
// environment (everything else should be pristine, just like it was on startup).
for (String name : **DEFAULT_PROPERTY_SOURCES**) {
if (input.getPropertySources().contains(name)) {
if (capturedPropertySources.contains(name)) {
capturedPropertySources.replace(name,
input.getPropertySources().get(name));
}
else {
capturedPropertySources.addLast(input.getPropertySources().get(name));
}
}
}
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 21 (7 by maintainers)
It’s the same code as you already have. E.g.
PropertySourceLocatorsdon’t get applied in time to use the properties they supply in the bootstrap phase (their purpose is, after all, to supply properties for the main application context), so you can’t use them withspring.cloud.config.discovery.enabled: truewhich is a bootstrap context feature. There might be an ordering issue that we can resolve in spring-cloud-commons (or maybe not), but you don’t need that right now - you can just re-implement your property source locators asEnvironmentPostProcessors. I tried it and it seems to work for me.