|
|
|
|
@ -21,6 +21,7 @@ import java.util.Arrays;
@@ -21,6 +21,7 @@ import java.util.Arrays;
|
|
|
|
|
import java.util.Collection; |
|
|
|
|
import java.util.Collections; |
|
|
|
|
import java.util.HashMap; |
|
|
|
|
import java.util.Iterator; |
|
|
|
|
import java.util.LinkedHashSet; |
|
|
|
|
import java.util.List; |
|
|
|
|
import java.util.Map; |
|
|
|
|
@ -33,26 +34,24 @@ import org.springframework.boot.SpringApplication;
@@ -33,26 +34,24 @@ import org.springframework.boot.SpringApplication;
|
|
|
|
|
import org.springframework.boot.SpringApplicationEnvironmentAvailableEvent; |
|
|
|
|
import org.springframework.boot.bind.PropertySourcesPropertyValues; |
|
|
|
|
import org.springframework.boot.bind.RelaxedDataBinder; |
|
|
|
|
import org.springframework.boot.config.PropertiesPropertySourceLoader; |
|
|
|
|
import org.springframework.boot.config.DefaultPropertySourceLoadersFactory; |
|
|
|
|
import org.springframework.boot.config.PropertySourceLoader; |
|
|
|
|
import org.springframework.boot.config.YamlPropertySourceLoader; |
|
|
|
|
import org.springframework.boot.config.PropertySourceLoadersFactory; |
|
|
|
|
import org.springframework.context.ApplicationListener; |
|
|
|
|
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; |
|
|
|
|
import org.springframework.context.annotation.PropertySources; |
|
|
|
|
import org.springframework.core.Ordered; |
|
|
|
|
import org.springframework.core.annotation.AnnotationAttributes; |
|
|
|
|
import org.springframework.core.annotation.AnnotationUtils; |
|
|
|
|
import org.springframework.core.convert.ConversionService; |
|
|
|
|
import org.springframework.core.convert.support.DefaultConversionService; |
|
|
|
|
import org.springframework.core.env.ConfigurableEnvironment; |
|
|
|
|
import org.springframework.core.env.Environment; |
|
|
|
|
import org.springframework.core.env.MutablePropertySources; |
|
|
|
|
import org.springframework.core.env.PropertySource; |
|
|
|
|
import org.springframework.core.env.StandardEnvironment; |
|
|
|
|
import org.springframework.core.io.DefaultResourceLoader; |
|
|
|
|
import org.springframework.core.io.Resource; |
|
|
|
|
import org.springframework.core.io.ResourceLoader; |
|
|
|
|
import org.springframework.core.type.AnnotationMetadata; |
|
|
|
|
import org.springframework.core.type.StandardAnnotationMetadata; |
|
|
|
|
import org.springframework.util.ClassUtils; |
|
|
|
|
import org.springframework.util.DigestUtils; |
|
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
|
|
|
|
|
|
@ -66,16 +65,13 @@ import org.springframework.util.StringUtils;
@@ -66,16 +65,13 @@ import org.springframework.util.StringUtils;
|
|
|
|
|
* <li>classpath:config/</li> |
|
|
|
|
* <li>file:./config/:</li> |
|
|
|
|
* </ul> |
|
|
|
|
* |
|
|
|
|
* <p> |
|
|
|
|
* Alternative locations and names can be specified using |
|
|
|
|
* {@link #setSearchLocations(String[])} and {@link #setNames(String)}. |
|
|
|
|
* |
|
|
|
|
* <p> |
|
|
|
|
* Additional files will also be loaded based on active profiles. For example if a 'web' |
|
|
|
|
* profile is active 'application-web.properties' and 'application-web.yml' will be |
|
|
|
|
* considered. |
|
|
|
|
* |
|
|
|
|
* <p> |
|
|
|
|
* The 'spring.config.name' property can be used to specify an alternative name to load or |
|
|
|
|
* alternatively the 'spring.config.location' property can be used to specify an exact |
|
|
|
|
@ -87,6 +83,8 @@ import org.springframework.util.StringUtils;
@@ -87,6 +83,8 @@ import org.springframework.util.StringUtils;
|
|
|
|
|
public class ConfigFileApplicationListener implements |
|
|
|
|
ApplicationListener<SpringApplicationEnvironmentAvailableEvent>, Ordered { |
|
|
|
|
|
|
|
|
|
private static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active"; |
|
|
|
|
|
|
|
|
|
private static final String LOCATION_VARIABLE = "${spring.config.location}"; |
|
|
|
|
|
|
|
|
|
private String[] searchLocations = new String[] { "classpath:", "file:./", |
|
|
|
|
@ -96,13 +94,13 @@ public class ConfigFileApplicationListener implements
@@ -96,13 +94,13 @@ public class ConfigFileApplicationListener implements
|
|
|
|
|
|
|
|
|
|
private int order = Integer.MIN_VALUE + 10; |
|
|
|
|
|
|
|
|
|
private final Map<String, PropertySource<?>> cached = new HashMap<String, PropertySource<?>>(); |
|
|
|
|
|
|
|
|
|
private final ConversionService conversionService = new DefaultConversionService(); |
|
|
|
|
|
|
|
|
|
private final PropertySourceAnnotations propertySourceAnnotations = new PropertySourceAnnotations(); |
|
|
|
|
private final Map<String, PropertySource<?>> cache = new HashMap<String, PropertySource<?>>(); |
|
|
|
|
|
|
|
|
|
private final PropertySourceAnnotations annotations = new PropertySourceAnnotations(); |
|
|
|
|
|
|
|
|
|
private PropertySourceLoaderFactory propertySourceLoaderFactory = new DefaultPropertySourceLoaderFactory(); |
|
|
|
|
private PropertySourceLoadersFactory propertySourceLoadersFactory = new DefaultPropertySourceLoadersFactory(); |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Binds the early {@link Environment} to the {@link SpringApplication}. This makes it |
|
|
|
|
@ -113,81 +111,63 @@ public class ConfigFileApplicationListener implements
@@ -113,81 +111,63 @@ public class ConfigFileApplicationListener implements
|
|
|
|
|
*/ |
|
|
|
|
@Override |
|
|
|
|
public void onApplicationEvent(SpringApplicationEnvironmentAvailableEvent event) { |
|
|
|
|
Environment created = event.getEnvironment(); |
|
|
|
|
if (created instanceof ConfigurableEnvironment) { |
|
|
|
|
SpringApplication springApplication = event.getSpringApplication(); |
|
|
|
|
extractPropertySources(springApplication.getSources()); |
|
|
|
|
ConfigurableEnvironment environment = (ConfigurableEnvironment) created; |
|
|
|
|
load(environment, new DefaultResourceLoader()); |
|
|
|
|
environment.getPropertySources().addAfter( |
|
|
|
|
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, |
|
|
|
|
new RandomValuePropertySource("random")); |
|
|
|
|
int before = springApplication.getSources().size(); |
|
|
|
|
// Set bean properties from the early environment
|
|
|
|
|
PropertyValues propertyValues = new PropertySourcesPropertyValues( |
|
|
|
|
environment.getPropertySources()); |
|
|
|
|
RelaxedDataBinder binder = new RelaxedDataBinder(springApplication, |
|
|
|
|
"spring.main"); |
|
|
|
|
binder.setConversionService(this.conversionService); |
|
|
|
|
binder.bind(propertyValues); |
|
|
|
|
int after = springApplication.getSources().size(); |
|
|
|
|
if (after > before) { |
|
|
|
|
// Do it again in case there are new @PropertySources
|
|
|
|
|
onApplicationEvent(event); |
|
|
|
|
} |
|
|
|
|
Environment environment = event.getEnvironment(); |
|
|
|
|
if (environment instanceof ConfigurableEnvironment) { |
|
|
|
|
configure((ConfigurableEnvironment) environment, event.getSpringApplication()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void extractPropertySources(Set<Object> sources) { |
|
|
|
|
for (Object source : sources) { |
|
|
|
|
if (source instanceof Class) { |
|
|
|
|
Class<?> type = (Class<?>) source; |
|
|
|
|
for (AnnotationAttributes propertySource : attributesForRepeatable( |
|
|
|
|
new StandardAnnotationMetadata(type), PropertySources.class, |
|
|
|
|
org.springframework.context.annotation.PropertySource.class)) { |
|
|
|
|
this.propertySourceAnnotations.add(type, |
|
|
|
|
propertySource.getStringArray("value"), |
|
|
|
|
propertySource.getBoolean("ignoreResourceNotFound"), |
|
|
|
|
propertySource.getString("name")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
private void configure(ConfigurableEnvironment environment, |
|
|
|
|
SpringApplication springApplication) { |
|
|
|
|
for (Object source : springApplication.getSources()) { |
|
|
|
|
this.annotations.addFromSource(source); |
|
|
|
|
} |
|
|
|
|
load(environment, new DefaultResourceLoader()); |
|
|
|
|
environment.getPropertySources().addAfter( |
|
|
|
|
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, |
|
|
|
|
new RandomValuePropertySource("random")); |
|
|
|
|
|
|
|
|
|
int sourcesSizeBefore = springApplication.getSources().size(); |
|
|
|
|
// Set bean properties from the early environment
|
|
|
|
|
PropertyValues propertyValues = new PropertySourcesPropertyValues( |
|
|
|
|
environment.getPropertySources()); |
|
|
|
|
RelaxedDataBinder binder = new RelaxedDataBinder(springApplication, "spring.main"); |
|
|
|
|
binder.setConversionService(this.conversionService); |
|
|
|
|
binder.bind(propertyValues); |
|
|
|
|
|
|
|
|
|
if (springApplication.getSources().size() > sourcesSizeBefore) { |
|
|
|
|
// Configure again in case there are new @PropertySources
|
|
|
|
|
configure(environment, springApplication); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
static Set<AnnotationAttributes> attributesForRepeatable(AnnotationMetadata metadata, |
|
|
|
|
Class<?> containerClass, Class<?> annotationClass) { |
|
|
|
|
Set<AnnotationAttributes> result = new LinkedHashSet<AnnotationAttributes>(); |
|
|
|
|
private void load(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { |
|
|
|
|
|
|
|
|
|
LoadCandidates candidates = new LoadCandidates(environment, resourceLoader); |
|
|
|
|
PropertySource<?> defaultProperties = environment.getPropertySources().remove( |
|
|
|
|
"defaultProperties"); |
|
|
|
|
|
|
|
|
|
addAttributesIfNotNull(result, |
|
|
|
|
metadata.getAnnotationAttributes(annotationClass.getName(), false)); |
|
|
|
|
String firstPropertySourceName = loadInitial(environment, resourceLoader, |
|
|
|
|
candidates); |
|
|
|
|
|
|
|
|
|
Map<String, Object> container = metadata.getAnnotationAttributes( |
|
|
|
|
containerClass.getName(), false); |
|
|
|
|
if (container != null && container.containsKey("value")) { |
|
|
|
|
for (Map<String, Object> containedAttributes : (Map<String, Object>[]) container |
|
|
|
|
.get("value")) { |
|
|
|
|
addAttributesIfNotNull(result, containedAttributes); |
|
|
|
|
if (environment.containsProperty(ACTIVE_PROFILES_PROPERTY)) { |
|
|
|
|
for (String activeProfile : StringUtils.commaDelimitedListToSet(environment |
|
|
|
|
.getProperty(ACTIVE_PROFILES_PROPERTY).toString())) { |
|
|
|
|
environment.addActiveProfile(activeProfile); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return Collections.unmodifiableSet(result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void addAttributesIfNotNull(Set<AnnotationAttributes> result, |
|
|
|
|
Map<String, Object> attributes) { |
|
|
|
|
if (attributes != null) { |
|
|
|
|
result.add(AnnotationAttributes.fromMap(attributes)); |
|
|
|
|
// Second load for specific profiles
|
|
|
|
|
loadAgain(environment, resourceLoader, candidates, firstPropertySourceName); |
|
|
|
|
|
|
|
|
|
if (defaultProperties != null) { |
|
|
|
|
environment.getPropertySources().addLast(defaultProperties); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void load(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { |
|
|
|
|
|
|
|
|
|
List<String> candidates = getCandidateLocations(environment, resourceLoader); |
|
|
|
|
Collections.reverse(candidates); |
|
|
|
|
PropertySource<?> removed = environment.getPropertySources().remove( |
|
|
|
|
"defaultProperties"); |
|
|
|
|
|
|
|
|
|
String first = null; |
|
|
|
|
private String loadInitial(ConfigurableEnvironment environment, |
|
|
|
|
ResourceLoader resourceLoader, LoadCandidates candidates) { |
|
|
|
|
String firstSourceName = null; |
|
|
|
|
// Initial load allows profiles to be activated
|
|
|
|
|
for (String candidate : candidates) { |
|
|
|
|
for (String path : StringUtils.commaDelimitedListToStringArray(environment |
|
|
|
|
@ -200,87 +180,51 @@ public class ConfigFileApplicationListener implements
@@ -200,87 +180,51 @@ public class ConfigFileApplicationListener implements
|
|
|
|
|
path = StringUtils.cleanPath(path); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
PropertySource<?> source = load(environment, resourceLoader, path, null); |
|
|
|
|
PropertySource<?> source = loadPropertySource(environment, |
|
|
|
|
resourceLoader, path, null); |
|
|
|
|
if (source != null) { |
|
|
|
|
if (first == null) { |
|
|
|
|
first = source.getName(); |
|
|
|
|
if (firstSourceName == null) { |
|
|
|
|
firstSourceName = source.getName(); |
|
|
|
|
} |
|
|
|
|
environment.getPropertySources().addLast(source); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (environment.containsProperty("spring.profiles.active")) { |
|
|
|
|
Set<String> profiles = StringUtils.commaDelimitedListToSet(environment |
|
|
|
|
.getProperty("spring.profiles.active").toString()); |
|
|
|
|
for (String active : profiles) { |
|
|
|
|
// allow document with no profile to set the active one
|
|
|
|
|
environment.addActiveProfile(active); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
return firstSourceName; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Second load for specific profiles
|
|
|
|
|
private void loadAgain(ConfigurableEnvironment environment, |
|
|
|
|
ResourceLoader resourceLoader, LoadCandidates candidates, |
|
|
|
|
String firstPropertySourceName) { |
|
|
|
|
for (String profile : environment.getActiveProfiles()) { |
|
|
|
|
for (String candidate : candidates) { |
|
|
|
|
PropertySource<?> source = load(environment, resourceLoader, candidate, |
|
|
|
|
profile); |
|
|
|
|
if (source != null) { |
|
|
|
|
if (first != null) { |
|
|
|
|
// Originals go at the end so they don't override the specific
|
|
|
|
|
// profiles
|
|
|
|
|
environment.getPropertySources().addBefore(first, source); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
environment.getPropertySources().addLast(source); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
PropertySource<?> source = loadPropertySource(environment, |
|
|
|
|
resourceLoader, candidate, profile); |
|
|
|
|
addBeforeOrLast(environment, firstPropertySourceName, source); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (removed != null) { |
|
|
|
|
environment.getPropertySources().addLast(removed); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private List<String> getCandidateLocations(ConfigurableEnvironment environment, |
|
|
|
|
ResourceLoader resourceLoader) { |
|
|
|
|
Set<String> candidates = new LinkedHashSet<String>(); |
|
|
|
|
for (String searchLocation : this.searchLocations) { |
|
|
|
|
for (String extension : new String[] { ".properties", ".yml" }) { |
|
|
|
|
for (String name : StringUtils |
|
|
|
|
.commaDelimitedListToStringArray(environment |
|
|
|
|
.resolvePlaceholders(this.names))) { |
|
|
|
|
String location = searchLocation + name + extension; |
|
|
|
|
candidates.add(location); |
|
|
|
|
} |
|
|
|
|
private void addBeforeOrLast(ConfigurableEnvironment environment, |
|
|
|
|
String relativePropertySourceName, PropertySource<?> source) { |
|
|
|
|
if (source != null) { |
|
|
|
|
MutablePropertySources propertySources = environment.getPropertySources(); |
|
|
|
|
// Originals go at the end so they don't override the specific profiles
|
|
|
|
|
if (relativePropertySourceName != null) { |
|
|
|
|
propertySources.addBefore(relativePropertySourceName, source); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
candidates.add(LOCATION_VARIABLE); |
|
|
|
|
/* |
|
|
|
|
* @PropertySource annotation locations go last here (eventually highest |
|
|
|
|
* priority). This unfortunately isn't the same semantics as @PropertySource in |
|
|
|
|
* Spring and it's hard to change that (so the property source gets added again in |
|
|
|
|
* last position by Spring later in the cycle). |
|
|
|
|
*/ |
|
|
|
|
for (String location : this.propertySourceAnnotations.locations()) { |
|
|
|
|
Resource resource = resourceLoader.getResource(location); |
|
|
|
|
if (!this.propertySourceAnnotations.ignoreResourceNotFound(location) |
|
|
|
|
&& !resource.exists()) { |
|
|
|
|
throw new IllegalStateException("Resource not found: " + location); |
|
|
|
|
else { |
|
|
|
|
propertySources.addLast(source); |
|
|
|
|
} |
|
|
|
|
candidates.add(location); |
|
|
|
|
} |
|
|
|
|
return new ArrayList<String>(candidates); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private PropertySource<?> load(ConfigurableEnvironment environment, |
|
|
|
|
private PropertySource<?> loadPropertySource(ConfigurableEnvironment environment, |
|
|
|
|
ResourceLoader resourceLoader, String location, String profile) { |
|
|
|
|
|
|
|
|
|
String suffix = "." + StringUtils.getFilenameExtension(location); |
|
|
|
|
Class<?> type = this.propertySourceAnnotations.configuration(location); |
|
|
|
|
Class<?> type = this.annotations.configuration(location); |
|
|
|
|
|
|
|
|
|
String suffix = "." + StringUtils.getFilenameExtension(location); |
|
|
|
|
if (StringUtils.hasLength(profile)) { |
|
|
|
|
location = location.replace(suffix, "-" + profile + suffix); |
|
|
|
|
} |
|
|
|
|
@ -289,71 +233,56 @@ public class ConfigFileApplicationListener implements
@@ -289,71 +233,56 @@ public class ConfigFileApplicationListener implements
|
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
List<PropertySourceLoader> loaders = this.propertySourceLoaderFactory |
|
|
|
|
.getLoaders(environment); |
|
|
|
|
|
|
|
|
|
Resource resource = resourceLoader.getResource(location); |
|
|
|
|
String name = this.propertySourceAnnotations.name(location); |
|
|
|
|
if (name == null) { |
|
|
|
|
name = location; |
|
|
|
|
} |
|
|
|
|
PropertySource<?> propertySource = getPropertySource(name, resource, profile, |
|
|
|
|
loaders); |
|
|
|
|
if (propertySource == null) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
return propertySource; |
|
|
|
|
String name = this.annotations.name(location); |
|
|
|
|
name = (name != null ? name : location); |
|
|
|
|
return getPropertySource(environment, name, resource, profile); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean isPropertySourceAnnotationOnExcludedType(Environment environment, |
|
|
|
|
String profile, Class<?> type, String location) { |
|
|
|
|
|
|
|
|
|
if (type == null) { |
|
|
|
|
// No configuration class to worry about, just a vanilla properties location
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (StringUtils.hasText(profile) |
|
|
|
|
&& !this.propertySourceAnnotations.locations().contains(location)) { |
|
|
|
|
&& !this.annotations.getLocations().contains(location)) { |
|
|
|
|
// We are looking for profile specific properties and this one isn't
|
|
|
|
|
// explicitly asked for in propertySourceAnnotations
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader( |
|
|
|
|
new DefaultListableBeanFactory(), environment); |
|
|
|
|
int before = reader.getRegistry().getBeanDefinitionCount(); |
|
|
|
|
reader.register(type); |
|
|
|
|
int after = reader.getRegistry().getBeanDefinitionCount(); |
|
|
|
|
if (after == before) { |
|
|
|
|
// The configuration class was @Conditional and excluded
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
// Return if the configuration class was @Conditional and excluded
|
|
|
|
|
return (after == before); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private PropertySource<?> getPropertySource(String name, Resource resource, |
|
|
|
|
String profile, List<PropertySourceLoader> loaders) { |
|
|
|
|
private PropertySource<?> getPropertySource(Environment environment, String name, |
|
|
|
|
Resource resource, String profile) { |
|
|
|
|
if (resource == null || !resource.exists()) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
String key = resource.getDescription() + (profile == null ? "" : "#" + profile); |
|
|
|
|
if (this.cached.containsKey(key)) { |
|
|
|
|
return this.cached.get(key); |
|
|
|
|
if (this.cache.containsKey(key)) { |
|
|
|
|
return this.cache.get(key); |
|
|
|
|
} |
|
|
|
|
boolean satisfied = true; |
|
|
|
|
for (PropertySourceLoader loader : loaders) { |
|
|
|
|
for (PropertySourceLoader loader : this.propertySourceLoadersFactory |
|
|
|
|
.getLoaders(environment)) { |
|
|
|
|
if (loader.supports(resource)) { |
|
|
|
|
PropertySource<?> propertySource = loader.load(name, resource); |
|
|
|
|
this.cached.put(key, propertySource); |
|
|
|
|
this.cache.put(key, propertySource); |
|
|
|
|
return propertySource; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
satisfied = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!satisfied) { |
|
|
|
|
throw new IllegalStateException( |
|
|
|
|
"No supported loader found for configuration resource: " + resource); |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
throw new IllegalStateException("No supported loader found for " |
|
|
|
|
+ "configuration resource: " + resource); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void setOrder(int order) { |
|
|
|
|
@ -381,13 +310,75 @@ public class ConfigFileApplicationListener implements
@@ -381,13 +310,75 @@ public class ConfigFileApplicationListener implements
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param propertySourceLoaderFactory the factory to set |
|
|
|
|
* Set the {@link PropertySourceLoadersFactory} that will be used to create |
|
|
|
|
* {@link PropertySourceLoader}s. |
|
|
|
|
*/ |
|
|
|
|
public void setPropertySourceLoadersFactory( |
|
|
|
|
PropertySourceLoadersFactory propertySourceLoaderFactory) { |
|
|
|
|
this.propertySourceLoadersFactory = propertySourceLoaderFactory; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Provides {@link Iterable} access to candidate property sources. |
|
|
|
|
*/ |
|
|
|
|
public void setPropertySourceLoaderFactory( |
|
|
|
|
PropertySourceLoaderFactory propertySourceLoaderFactory) { |
|
|
|
|
this.propertySourceLoaderFactory = propertySourceLoaderFactory; |
|
|
|
|
private class LoadCandidates implements Iterable<String> { |
|
|
|
|
|
|
|
|
|
private final List<String> candidates; |
|
|
|
|
|
|
|
|
|
public LoadCandidates(ConfigurableEnvironment environment, |
|
|
|
|
ResourceLoader resourceLoader) { |
|
|
|
|
Set<String> candidates = new LinkedHashSet<String>(); |
|
|
|
|
addLoadCandidatesFromSearchLocations(environment, candidates); |
|
|
|
|
candidates.add(LOCATION_VARIABLE); |
|
|
|
|
// @PropertySource annotation locations go last here (eventually highest
|
|
|
|
|
// priority). This unfortunately isn't the same semantics as @PropertySource
|
|
|
|
|
// in
|
|
|
|
|
// Spring and it's hard to change that (so the property source gets added
|
|
|
|
|
// again in
|
|
|
|
|
// last position by Spring later in the cycle).
|
|
|
|
|
addLoadCandidatesFromAnnotations(resourceLoader, candidates); |
|
|
|
|
this.candidates = new ArrayList<String>(candidates); |
|
|
|
|
Collections.reverse(this.candidates); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void addLoadCandidatesFromSearchLocations( |
|
|
|
|
ConfigurableEnvironment environment, Set<String> candidates) { |
|
|
|
|
for (String location : ConfigFileApplicationListener.this.searchLocations) { |
|
|
|
|
for (String extension : new String[] { ".properties", ".yml" }) { |
|
|
|
|
for (String name : StringUtils |
|
|
|
|
.commaDelimitedListToStringArray(environment |
|
|
|
|
.resolvePlaceholders(ConfigFileApplicationListener.this.names))) { |
|
|
|
|
candidates.add(location + name + extension); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void addLoadCandidatesFromAnnotations(ResourceLoader resourceLoader, |
|
|
|
|
Set<String> candidates) { |
|
|
|
|
for (String location : ConfigFileApplicationListener.this.annotations |
|
|
|
|
.getLocations()) { |
|
|
|
|
Resource resource = resourceLoader.getResource(location); |
|
|
|
|
if (!ConfigFileApplicationListener.this.annotations |
|
|
|
|
.ignoreResourceNotFound(location) && !resource.exists()) { |
|
|
|
|
throw new IllegalStateException("Resource not found: " + location); |
|
|
|
|
} |
|
|
|
|
candidates.add(location); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Iterator<String> iterator() { |
|
|
|
|
return this.candidates.iterator(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* {@link PropertySource} that returns a random value for any property that starts |
|
|
|
|
* with {@literal "random."}. Return a {@code byte[]} unless the property name ends |
|
|
|
|
* with {@literal ".int} or {@literal ".long"}. |
|
|
|
|
*/ |
|
|
|
|
private static class RandomValuePropertySource extends PropertySource<Random> { |
|
|
|
|
|
|
|
|
|
public RandomValuePropertySource(String name) { |
|
|
|
|
@ -412,6 +403,10 @@ public class ConfigFileApplicationListener implements
@@ -412,6 +403,10 @@ public class ConfigFileApplicationListener implements
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Holds details collected from |
|
|
|
|
* {@link org.springframework.context.annotation.PropertySource} annotations. |
|
|
|
|
*/ |
|
|
|
|
private static class PropertySourceAnnotations { |
|
|
|
|
|
|
|
|
|
private final Collection<String> locations = new LinkedHashSet<String>(); |
|
|
|
|
@ -422,16 +417,30 @@ public class ConfigFileApplicationListener implements
@@ -422,16 +417,30 @@ public class ConfigFileApplicationListener implements
|
|
|
|
|
|
|
|
|
|
private final Map<String, Boolean> ignores = new HashMap<String, Boolean>(); |
|
|
|
|
|
|
|
|
|
public void add(Class<?> source, String[] locations, |
|
|
|
|
boolean ignoreResourceNotFound, String name) { |
|
|
|
|
this.locations.addAll(Arrays.asList(locations)); |
|
|
|
|
if (StringUtils.hasText(name)) { |
|
|
|
|
for (String location : locations) { |
|
|
|
|
this.names.put(location, name); |
|
|
|
|
public void addFromSource(Object source) { |
|
|
|
|
if (source instanceof Class<?>) { |
|
|
|
|
addFromSource((Class<?>) source); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void addFromSource(Class<?> source) { |
|
|
|
|
for (org.springframework.context.annotation.PropertySource propertySource : AnnotationUtils |
|
|
|
|
.getRepeatableAnnotation(source, PropertySources.class, |
|
|
|
|
org.springframework.context.annotation.PropertySource.class)) { |
|
|
|
|
add(source, propertySource); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void add(Class<?> source, |
|
|
|
|
org.springframework.context.annotation.PropertySource annotation) { |
|
|
|
|
this.locations.addAll(Arrays.asList(annotation.value())); |
|
|
|
|
if (StringUtils.hasText(annotation.name())) { |
|
|
|
|
for (String location : annotation.value()) { |
|
|
|
|
this.names.put(location, annotation.name()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (String location : locations) { |
|
|
|
|
boolean reallyIgnore = ignoreResourceNotFound; |
|
|
|
|
for (String location : annotation.value()) { |
|
|
|
|
boolean reallyIgnore = annotation.ignoreResourceNotFound(); |
|
|
|
|
if (this.ignores.containsKey(location)) { |
|
|
|
|
// Only if they all ignore this location will it be ignored
|
|
|
|
|
reallyIgnore &= this.ignores.get(location); |
|
|
|
|
@ -446,8 +455,7 @@ public class ConfigFileApplicationListener implements
@@ -446,8 +455,7 @@ public class ConfigFileApplicationListener implements
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public boolean ignoreResourceNotFound(String location) { |
|
|
|
|
return this.ignores.containsKey(location) ? this.ignores.get(location) |
|
|
|
|
: false; |
|
|
|
|
return Boolean.TRUE.equals(this.ignores.get(location)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public String name(String location) { |
|
|
|
|
@ -459,29 +467,9 @@ public class ConfigFileApplicationListener implements
@@ -459,29 +467,9 @@ public class ConfigFileApplicationListener implements
|
|
|
|
|
return "boot." + name; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Collection<String> locations() { |
|
|
|
|
public Collection<String> getLocations() { |
|
|
|
|
return this.locations; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static interface PropertySourceLoaderFactory { |
|
|
|
|
List<PropertySourceLoader> getLoaders(Environment environment); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static class DefaultPropertySourceLoaderFactory implements |
|
|
|
|
PropertySourceLoaderFactory { |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public List<PropertySourceLoader> getLoaders(Environment environment) { |
|
|
|
|
ArrayList<PropertySourceLoader> loaders = new ArrayList<PropertySourceLoader>(); |
|
|
|
|
loaders.add(new PropertiesPropertySourceLoader()); |
|
|
|
|
if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) { |
|
|
|
|
loaders.add(YamlPropertySourceLoader.springProfileAwareLoader(environment |
|
|
|
|
.getActiveProfiles())); |
|
|
|
|
} |
|
|
|
|
return loaders; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|