Commit c0d79b92 authored by Phillip Webb's avatar Phillip Webb

Rationalize multi-document config file handling

Update `PropertySourceLoader` so that it no longer needs to deal with
matching multi-document files using the `spring.profile` property. The
loader now simply returns one or more `PropertSource` instances for a
given `Resource`.

All property matching now occurs in the `ConfigFileApplicationListener`.
This allows document processing logic to be contained in a single place,
and allows us to rationalize the algorithm so that negative matching
profiles are processed last.

Fixes gh-12159
parent 3d8f760e
...@@ -22,7 +22,6 @@ import org.springframework.boot.SpringApplication; ...@@ -22,7 +22,6 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
...@@ -41,17 +40,16 @@ public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor ...@@ -41,17 +40,16 @@ public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor
public void postProcessEnvironment(ConfigurableEnvironment environment, public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) { SpringApplication application) {
Resource path = new ClassPathResource("com/example/myapp/config.yml"); Resource path = new ClassPathResource("com/example/myapp/config.yml");
PropertySource<?> propertySource = loadYaml(path, environment); PropertySource<?> propertySource = loadYaml(path);
environment.getPropertySources().addLast(propertySource); environment.getPropertySources().addLast(propertySource);
} }
private PropertySource<?> loadYaml(Resource path, Environment environment) { private PropertySource<?> loadYaml(Resource path) {
if (!path.exists()) { if (!path.exists()) {
throw new IllegalArgumentException("Resource " + path + " does not exist"); throw new IllegalArgumentException("Resource " + path + " does not exist");
} }
try { try {
return this.loader.load("custom-resource", path, null, return this.loader.load("custom-resource", path).get(0);
environment::acceptsProfiles);
} }
catch (IOException ex) { catch (IOException ex) {
throw new IllegalStateException( throw new IllegalStateException(
......
...@@ -180,10 +180,10 @@ public class PropertiesMigrationReporterTests { ...@@ -180,10 +180,10 @@ public class PropertiesMigrationReporterTests {
private PropertySource<?> loadPropertySource(String name, String path) private PropertySource<?> loadPropertySource(String name, String path)
throws IOException { throws IOException {
ClassPathResource resource = new ClassPathResource(path); ClassPathResource resource = new ClassPathResource(path);
PropertySource<?> propertySource = new PropertiesPropertySourceLoader().load(name, List<PropertySource<?>> propertySources = new PropertiesPropertySourceLoader()
resource, null, (profile) -> true); .load(name, resource);
assertThat(propertySource).isNotNull(); assertThat(propertySources).isNotEmpty();
return propertySource; return propertySources.get(0);
} }
private ConfigurationMetadataRepository loadRepository(String... content) { private ConfigurationMetadataRepository loadRepository(String... content) {
......
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.env;
import java.util.function.Predicate;
import org.springframework.util.ObjectUtils;
/**
* {@link SpringProfilesDocumentMatcher} that tests if a profile is accepted.
*
* @author Phillip Webb
*/
class AcceptsProfilesDocumentMatcher extends SpringProfilesDocumentMatcher {
private final Predicate<String[]> acceptsProfiles;
AcceptsProfilesDocumentMatcher(Predicate<String[]> acceptsProfiles) {
this.acceptsProfiles = acceptsProfiles;
}
@Override
protected boolean matches(String[] profiles) {
return ObjectUtils.isEmpty(profiles) || this.acceptsProfiles.test(profiles);
}
}
...@@ -16,9 +16,9 @@ ...@@ -16,9 +16,9 @@
package org.springframework.boot.env; package org.springframework.boot.env;
import java.util.LinkedHashMap; import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -53,11 +53,8 @@ class OriginTrackedYamlLoader extends YamlProcessor { ...@@ -53,11 +53,8 @@ class OriginTrackedYamlLoader extends YamlProcessor {
private final Resource resource; private final Resource resource;
OriginTrackedYamlLoader(Resource resource, String profileToLoad, OriginTrackedYamlLoader(Resource resource) {
Predicate<String[]> acceptsProfiles) {
this.resource = resource; this.resource = resource;
setDocumentMatchers(new ProfileToLoadDocumentMatcher(profileToLoad),
new AcceptsProfilesDocumentMatcher(acceptsProfiles));
setResources(resource); setResources(resource);
} }
...@@ -70,9 +67,11 @@ class OriginTrackedYamlLoader extends YamlProcessor { ...@@ -70,9 +67,11 @@ class OriginTrackedYamlLoader extends YamlProcessor {
return new Yaml(constructor, representer, dumperOptions, resolver); return new Yaml(constructor, representer, dumperOptions, resolver);
} }
public Map<String, Object> load() { public List<Map<String, Object>> load() {
final Map<String, Object> result = new LinkedHashMap<>(); final List<Map<String, Object>> result = new ArrayList<>();
process((properties, map) -> result.putAll(getFlattenedMap(map))); process((properties, map) -> {
result.add(getFlattenedMap(map));
});
return result; return result;
} }
......
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.env;
import java.util.Arrays;
import org.springframework.util.ObjectUtils;
/**
* {@link SpringProfilesDocumentMatcher} that matches a specific profile to load.
*
* @author Phillip Webb
*/
class ProfileToLoadDocumentMatcher extends SpringProfilesDocumentMatcher {
private final String profile;
ProfileToLoadDocumentMatcher(String profile) {
this.profile = profile;
}
@Override
protected boolean matches(String[] profiles) {
String[] positiveProfiles = (profiles == null ? null : Arrays.stream(profiles)
.filter(this::isPositiveProfile).toArray(String[]::new));
if (this.profile == null) {
return ObjectUtils.isEmpty(positiveProfiles);
}
return ObjectUtils.containsElement(positiveProfiles, this.profile);
}
private boolean isPositiveProfile(String profile) {
return !profile.startsWith("!");
}
}
...@@ -17,8 +17,9 @@ ...@@ -17,8 +17,9 @@
package org.springframework.boot.env; package org.springframework.boot.env;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
...@@ -41,15 +42,14 @@ public class PropertiesPropertySourceLoader implements PropertySourceLoader { ...@@ -41,15 +42,14 @@ public class PropertiesPropertySourceLoader implements PropertySourceLoader {
} }
@Override @Override
public PropertySource<?> load(String name, Resource resource, String profileToLoad, public List<PropertySource<?>> load(String name, Resource resource)
Predicate<String[]> acceptsProfiles) throws IOException { throws IOException {
if (profileToLoad == null) { Map<String, ?> properties = loadProperties(resource);
Map<String, ?> properties = loadProperties(resource); if (properties.isEmpty()) {
if (!properties.isEmpty()) { return Collections.emptyList();
return new OriginTrackedMapPropertySource(name, properties);
}
} }
return null; return Collections
.singletonList(new OriginTrackedMapPropertySource(name, properties));
} }
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
package org.springframework.boot.env; package org.springframework.boot.env;
import java.io.IOException; import java.io.IOException;
import java.util.function.Predicate; import java.util.List;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
...@@ -39,17 +39,15 @@ public interface PropertySourceLoader { ...@@ -39,17 +39,15 @@ public interface PropertySourceLoader {
String[] getFileExtensions(); String[] getFileExtensions();
/** /**
* Load the resource into a property source. * Load the resource into one or more property sources. Implementations may either
* @param name the name of the property source * return a list containing a single source, or in the case of a multi-document format
* such as yaml a source or each document in the resource.
* @param name the root name of the property source. If multiple documents are loaded
* an additional suffix should be added to the name for each source loaded.
* @param resource the resource to load * @param resource the resource to load
* @param profileToLoad the name of the profile to load or {@code null}. The profile * @return a list property sources
* can be used to load multi-document files (such as YAML). Simple property formats
* should {@code null} when asked to load a profile.
* @param acceptsProfiles predicate to determine if a particular profile is accepted
* @return a property source or {@code null}
* @throws IOException if the source cannot be loaded * @throws IOException if the source cannot be loaded
*/ */
PropertySource<?> load(String name, Resource resource, String profileToLoad, List<PropertySource<?>> load(String name, Resource resource) throws IOException;
Predicate<String[]> acceptsProfiles) throws IOException;
} }
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.env;
import java.util.Map;
import java.util.Properties;
import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher;
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationProperty;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.boot.origin.OriginTrackedValue;
/**
* Base class for {@link DocumentMatcher DocumentMatchers} that check the
* {@code spring.profiles} property.
*
* @author Phillip Webb
* @see OriginTrackedYamlLoader
*/
abstract class SpringProfilesDocumentMatcher implements DocumentMatcher {
@Override
public final MatchStatus matches(Properties properties) {
Binder binder = new Binder(
new OriginTrackedValueConfigurationPropertySource(properties));
String[] profiles = binder.bind("spring.profiles", Bindable.of(String[].class))
.orElse(null);
return (matches(profiles) ? MatchStatus.ABSTAIN : MatchStatus.NOT_FOUND);
}
protected abstract boolean matches(String[] profiles);
/**
* {@link MapConfigurationPropertySource} that deals with unwrapping
* {@link OriginTrackedValue OriginTrackedValues} from the underlying map.
*/
static class OriginTrackedValueConfigurationPropertySource
extends MapConfigurationPropertySource {
OriginTrackedValueConfigurationPropertySource(Map<?, ?> map) {
super(map);
}
@Override
public ConfigurationProperty getConfigurationProperty(
ConfigurationPropertyName name) {
ConfigurationProperty property = super.getConfigurationProperty(name);
if (property != null && property.getValue() instanceof OriginTrackedValue) {
OriginTrackedValue originTrackedValue = (OriginTrackedValue) property
.getValue();
property = new ConfigurationProperty(property.getName(),
originTrackedValue.getValue(), originTrackedValue.getOrigin());
}
return property;
}
}
}
...@@ -17,8 +17,10 @@ ...@@ -17,8 +17,10 @@
package org.springframework.boot.env; package org.springframework.boot.env;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
...@@ -39,18 +41,23 @@ public class YamlPropertySourceLoader implements PropertySourceLoader { ...@@ -39,18 +41,23 @@ public class YamlPropertySourceLoader implements PropertySourceLoader {
} }
@Override @Override
public PropertySource<?> load(String name, Resource resource, String profileToLoad, public List<PropertySource<?>> load(String name, Resource resource)
Predicate<String[]> acceptsProfiles) throws IOException { throws IOException {
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) { if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
throw new IllegalStateException("Attempted to load " + name throw new IllegalStateException("Attempted to load " + name
+ " but snakeyaml was not found on the classpath"); + " but snakeyaml was not found on the classpath");
} }
Map<String, Object> source = new OriginTrackedYamlLoader(resource, profileToLoad, List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
acceptsProfiles).load(); if (loaded.isEmpty()) {
if (!source.isEmpty()) { return Collections.emptyList();
return new OriginTrackedMapPropertySource(name, source);
} }
return null; List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
for (int i = 0; i < loaded.size(); i++) {
propertySources.add(new OriginTrackedMapPropertySource(
name + (loaded.size() == 1 ? "" : " (document #" + i + ")"),
loaded.get(i)));
}
return propertySources;
} }
} }
...@@ -540,8 +540,8 @@ public class ConfigFileApplicationListenerTests { ...@@ -540,8 +540,8 @@ public class ConfigFileApplicationListenerTests {
.map(org.springframework.core.env.PropertySource::getName) .map(org.springframework.core.env.PropertySource::getName)
.collect(Collectors.toList()); .collect(Collectors.toList());
assertThat(names).contains( assertThat(names).contains(
"applicationConfig: [classpath:/testsetprofiles.yml]#dev", "applicationConfig: [classpath:/testsetprofiles.yml] (document #0)",
"applicationConfig: [classpath:/testsetprofiles.yml]"); "applicationConfig: [classpath:/testsetprofiles.yml] (document #1)");
} }
@Test @Test
......
...@@ -88,6 +88,48 @@ public class ConfigFileApplicationListenerYamlProfileNegationTests { ...@@ -88,6 +88,48 @@ public class ConfigFileApplicationListenerYamlProfileNegationTests {
assertVersionProperty(this.context, "NOT A", "C", "B"); assertVersionProperty(this.context, "NOT A", "C", "B");
} }
@Test
public void yamlProfileCascading() {
SpringApplication application = new SpringApplication(Config.class);
application.setWebApplicationType(WebApplicationType.NONE);
String configName = "--spring.config.name=cascadingprofiles";
this.context = application.run(configName);
assertVersionProperty(this.context, "E", "D", "C", "E", "A", "B");
assertThat(this.context.getEnvironment().getProperty("not-a")).isNull();
assertThat(this.context.getEnvironment().getProperty("not-b")).isNull();
assertThat(this.context.getEnvironment().getProperty("not-c")).isNull();
assertThat(this.context.getEnvironment().getProperty("not-d")).isNull();
assertThat(this.context.getEnvironment().getProperty("not-e")).isNull();
}
@Test
public void yamlProfileCascadingOverrideProfilesA() {
SpringApplication application = new SpringApplication(Config.class);
application.setWebApplicationType(WebApplicationType.NONE);
String configName = "--spring.config.name=cascadingprofiles";
this.context = application.run(configName, "--spring.profiles.active=A");
assertVersionProperty(this.context, "E", "C", "E", "A");
assertThat(this.context.getEnvironment().getProperty("not-a")).isNull();
assertThat(this.context.getEnvironment().getProperty("not-b")).isEqualTo("true");
assertThat(this.context.getEnvironment().getProperty("not-c")).isNull();
assertThat(this.context.getEnvironment().getProperty("not-d")).isEqualTo("true");
assertThat(this.context.getEnvironment().getProperty("not-e")).isNull();
}
@Test
public void yamlProfileCascadingOverrideProfilesB() {
SpringApplication application = new SpringApplication(Config.class);
application.setWebApplicationType(WebApplicationType.NONE);
String configName = "--spring.config.name=cascadingprofiles";
this.context = application.run(configName, "--spring.profiles.active=B");
assertVersionProperty(this.context, "E", "D", "E", "B");
assertThat(this.context.getEnvironment().getProperty("not-a")).isEqualTo("true");
assertThat(this.context.getEnvironment().getProperty("not-b")).isNull();
assertThat(this.context.getEnvironment().getProperty("not-c")).isEqualTo("true");
assertThat(this.context.getEnvironment().getProperty("not-d")).isNull();
assertThat(this.context.getEnvironment().getProperty("not-e")).isNull();
}
private void assertVersionProperty(ConfigurableApplicationContext context, private void assertVersionProperty(ConfigurableApplicationContext context,
String expectedVersion, String... expectedActiveProfiles) { String expectedVersion, String... expectedActiveProfiles) {
assertThat(context.getEnvironment().getActiveProfiles()) assertThat(context.getEnvironment().getActiveProfiles())
......
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.env;
import java.util.Properties;
import java.util.function.Predicate;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Tests for {@link AcceptsProfilesDocumentMatcher}.
*
* @author Phillip Webb
*/
public class AcceptsProfilesDocumentMatcherTests {
@Mock
private Predicate<String[]> acceptsProfiles;
private AcceptsProfilesDocumentMatcher matcher;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.matcher = new AcceptsProfilesDocumentMatcher(this.acceptsProfiles);
}
@Test
public void matchesWhenHasNoProfilePropertyShouldReturnAbstain() {
Properties properties = new Properties();
assertThat(this.matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
}
@Test
public void matchesWhenAcceptsProfileShouldReturnAbstain() {
Properties properties = new Properties();
properties.put("spring.profiles", "foo");
given(this.acceptsProfiles.test(new String[] { "foo" })).willReturn(true);
assertThat(this.matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
}
@Test
public void matchesWhenDoesNotAcceptProfileShouldReturnNotFound() {
Properties properties = new Properties();
properties.put("spring.profiles", "foo");
given(this.acceptsProfiles.test(new String[] { "foo" })).willReturn(false);
assertThat(this.matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
}
}
...@@ -46,7 +46,7 @@ public class NoSnakeYamlPropertySourceLoaderTests { ...@@ -46,7 +46,7 @@ public class NoSnakeYamlPropertySourceLoaderTests {
"Attempted to load resource but snakeyaml was not found on the classpath"); "Attempted to load resource but snakeyaml was not found on the classpath");
ByteArrayResource resource = new ByteArrayResource( ByteArrayResource resource = new ByteArrayResource(
"foo:\n bar: spam".getBytes()); "foo:\n bar: spam".getBytes());
this.loader.load("resource", resource, null, (profile) -> true); this.loader.load("resource", resource);
} }
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.env; package org.springframework.boot.env;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.junit.Before; import org.junit.Before;
...@@ -38,12 +39,12 @@ public class OriginTrackedYamlLoaderTests { ...@@ -38,12 +39,12 @@ public class OriginTrackedYamlLoaderTests {
private OriginTrackedYamlLoader loader; private OriginTrackedYamlLoader loader;
private Map<String, Object> result; private List<Map<String, Object>> result;
@Before @Before
public void setUp() { public void setUp() {
Resource resource = new ClassPathResource("test-yaml.yml", getClass()); Resource resource = new ClassPathResource("test-yaml.yml", getClass());
this.loader = new OriginTrackedYamlLoader(resource, null, (profile) -> true); this.loader = new OriginTrackedYamlLoader(resource);
} }
@Test @Test
...@@ -90,15 +91,6 @@ public class OriginTrackedYamlLoaderTests { ...@@ -90,15 +91,6 @@ public class OriginTrackedYamlLoaderTests {
assertThat(getLocation(education)).isEqualTo("16:12"); assertThat(getLocation(education)).isEqualTo("16:12");
} }
@Test
public void processWithActiveProfile() {
Resource resource = new ClassPathResource("test-yaml.yml", getClass());
this.loader = new OriginTrackedYamlLoader(resource, "development",
(profile) -> true);
Map<String, Object> result = this.loader.load();
assertThat(result.get("name").toString()).isEqualTo("Test Name");
}
@Test @Test
public void processListOfMaps() { public void processListOfMaps() {
OriginTrackedValue name = getValue("example.foo[0].name"); OriginTrackedValue name = getValue("example.foo[0].name");
...@@ -129,7 +121,7 @@ public class OriginTrackedYamlLoaderTests { ...@@ -129,7 +121,7 @@ public class OriginTrackedYamlLoaderTests {
if (this.result == null) { if (this.result == null) {
this.result = this.loader.load(); this.result = this.loader.load();
} }
return (OriginTrackedValue) this.result.get(name); return (OriginTrackedValue) this.result.get(0).get(name);
} }
private String getLocation(OriginTrackedValue value) { private String getLocation(OriginTrackedValue value) {
......
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.env;
import java.util.Properties;
import org.junit.Test;
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ProfileToLoadDocumentMatcher}.
*
* @author Phillip Webb
*/
public class ProfileToLoadDocumentMatcherTests {
@Test
public void matchesWhenProfilesIsNullAndHasNoProfilePropertiesShouldReturnAbstain() {
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher(null);
Properties properties = new Properties();
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
}
@Test
public void matchesWhenProfileIsNullAndHasOnlyNegativeProfilePropertiesShouldReturnAbstain() {
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher(null);
Properties properties = new Properties();
properties.put("spring.profiles", "!foo,!bar");
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
}
@Test
public void matchesWhenProfileIsNullAndHasProfilePropertyShouldReturnNotFound() {
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher(null);
Properties properties = new Properties();
properties.put("spring.profiles", "!foo,!bar,baz");
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
}
@Test
public void matchesWhenProfilesIsSetAndHasNoProfilePropertiesShouldReturnNotFound() {
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher("bar");
Properties properties = new Properties();
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
}
@Test
public void matchesWhenProfileIsSetAndHasOnlyNegativeProfilePropertiesShouldReturnNotFound() {
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher("bar");
Properties properties = new Properties();
properties.put("spring.profiles", "!foo,!bar,baz");
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
}
@Test
public void matchesWhenProfileIsSetAndHasProfilePropertyShouldReturnAbstain() {
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher("bar");
Properties properties = new Properties();
properties.put("spring.profiles", "!foo,bar,baz");
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
}
}
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.env; package org.springframework.boot.env;
import java.util.List;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
...@@ -41,17 +43,17 @@ public class PropertiesPropertySourceLoaderTests { ...@@ -41,17 +43,17 @@ public class PropertiesPropertySourceLoaderTests {
@Test @Test
public void loadProperties() throws Exception { public void loadProperties() throws Exception {
PropertySource<?> source = this.loader.load("test.properties", List<PropertySource<?>> loaded = this.loader.load("test.properties",
new ClassPathResource("test-properties.properties", getClass()), null, new ClassPathResource("test-properties.properties", getClass()));
(profile) -> true); PropertySource<?> source = loaded.get(0);
assertThat(source.getProperty("test")).isEqualTo("properties"); assertThat(source.getProperty("test")).isEqualTo("properties");
} }
@Test @Test
public void loadXml() throws Exception { public void loadXml() throws Exception {
PropertySource<?> source = this.loader.load("test.xml", List<PropertySource<?>> loaded = this.loader.load("test.xml",
new ClassPathResource("test-xml.xml", getClass()), null, new ClassPathResource("test-xml.xml", getClass()));
(profile) -> true); PropertySource<?> source = loaded.get(0);
assertThat(source.getProperty("test")).isEqualTo("xml"); assertThat(source.getProperty("test")).isEqualTo("xml");
} }
......
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.env;
import java.util.Properties;
import org.junit.Test;
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
import org.springframework.boot.origin.OriginTrackedValue;
import org.springframework.util.ObjectUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SpringProfilesDocumentMatcher}.
*
* @author Phillip Webb
*/
public class SpringProfilesDocumentMatcherTests {
private TestSpringProfilesDocumentMatcher matcher = new TestSpringProfilesDocumentMatcher();
@Test
public void matchesShouldBindAgainstCommaList() {
Properties properties = new Properties();
properties.put("spring.profiles", "foo,bar");
this.matcher.matches(properties);
assertThat(this.matcher.getProfiles()).containsExactly("foo", "bar");
}
@Test
public void matchesShouldBindAgainstYamlList() {
Properties properties = new Properties();
properties.put("spring.profiles[0]", "foo");
properties.put("spring.profiles[1]", "bar");
this.matcher.matches(properties);
assertThat(this.matcher.getProfiles()).containsExactly("foo", "bar");
}
@Test
public void matchesShouldBindAgainstOriginTrackedValue() {
Properties properties = new Properties();
properties.put("spring.profiles", OriginTrackedValue.of("foo,bar"));
this.matcher.matches(properties);
assertThat(this.matcher.getProfiles()).containsExactly("foo", "bar");
}
@Test
public void matchesWhenMatchShouldReturnAbstain() {
Properties properties = new Properties();
properties.put("spring.profiles", "foo,bar");
assertThat(this.matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
}
@Test
public void matchesWhenNoMatchShouldReturnNotFound() {
Properties properties = new Properties();
assertThat(this.matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
}
private static class TestSpringProfilesDocumentMatcher
extends SpringProfilesDocumentMatcher {
private String[] profiles;
@Override
protected boolean matches(String[] profiles) {
this.profiles = profiles;
return !ObjectUtils.isEmpty(profiles);
}
public String[] getProfiles() {
return this.profiles;
}
}
}
...@@ -26,7 +26,6 @@ import org.springframework.core.env.PropertySource; ...@@ -26,7 +26,6 @@ import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -46,8 +45,7 @@ public class YamlPropertySourceLoaderTests { ...@@ -46,8 +45,7 @@ public class YamlPropertySourceLoaderTests {
public void load() throws Exception { public void load() throws Exception {
ByteArrayResource resource = new ByteArrayResource( ByteArrayResource resource = new ByteArrayResource(
"foo:\n bar: spam".getBytes()); "foo:\n bar: spam".getBytes());
PropertySource<?> source = this.loader.load("resource", resource, null, PropertySource<?> source = this.loader.load("resource", resource).get(0);
(profile) -> true);
assertThat(source).isNotNull(); assertThat(source).isNotNull();
assertThat(source.getProperty("foo.bar")).isEqualTo("spam"); assertThat(source.getProperty("foo.bar")).isEqualTo("spam");
} }
...@@ -62,7 +60,7 @@ public class YamlPropertySourceLoaderTests { ...@@ -62,7 +60,7 @@ public class YamlPropertySourceLoaderTests {
} }
ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes()); ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes());
EnumerablePropertySource<?> source = (EnumerablePropertySource<?>) this.loader EnumerablePropertySource<?> source = (EnumerablePropertySource<?>) this.loader
.load("resource", resource, null, (profile) -> true); .load("resource", resource).get(0);
assertThat(source).isNotNull(); assertThat(source).isNotNull();
assertThat(source.getPropertyNames()) assertThat(source.getPropertyNames())
.isEqualTo(StringUtils.toStringArray(expected)); .isEqualTo(StringUtils.toStringArray(expected));
...@@ -75,18 +73,16 @@ public class YamlPropertySourceLoaderTests { ...@@ -75,18 +73,16 @@ public class YamlPropertySourceLoaderTests {
yaml.append("---\n"); yaml.append("---\n");
yaml.append("foo:\n baz: wham\n"); yaml.append("foo:\n baz: wham\n");
ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes()); ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes());
PropertySource<?> source = this.loader.load("resource", resource, null, List<PropertySource<?>> loaded = this.loader.load("resource", resource);
(profile) -> true); assertThat(loaded).hasSize(2);
assertThat(source).isNotNull(); assertThat(loaded.get(0).getProperty("foo.bar")).isEqualTo("spam");
assertThat(source.getProperty("foo.bar")).isEqualTo("spam"); assertThat(loaded.get(1).getProperty("foo.baz")).isEqualTo("wham");
assertThat(source.getProperty("foo.baz")).isEqualTo("wham");
} }
@Test @Test
public void timestampLikeItemsDoNotBecomeDates() throws Exception { public void timestampLikeItemsDoNotBecomeDates() throws Exception {
ByteArrayResource resource = new ByteArrayResource("foo: 2015-01-28".getBytes()); ByteArrayResource resource = new ByteArrayResource("foo: 2015-01-28".getBytes());
PropertySource<?> source = this.loader.load("resource", resource, null, PropertySource<?> source = this.loader.load("resource", resource).get(0);
(profile) -> true);
assertThat(source).isNotNull(); assertThat(source).isNotNull();
assertThat(source.getProperty("foo")).isEqualTo("2015-01-28"); assertThat(source.getProperty("foo")).isEqualTo("2015-01-28");
} }
...@@ -94,42 +90,13 @@ public class YamlPropertySourceLoaderTests { ...@@ -94,42 +90,13 @@ public class YamlPropertySourceLoaderTests {
@Test @Test
public void loadOriginAware() throws Exception { public void loadOriginAware() throws Exception {
Resource resource = new ClassPathResource("test-yaml.yml", getClass()); Resource resource = new ClassPathResource("test-yaml.yml", getClass());
PropertySource<?> source = this.loader.load("resource", resource, null, List<PropertySource<?>> loaded = this.loader.load("resource", resource);
(profile) -> true); for (PropertySource<?> source : loaded) {
EnumerablePropertySource<?> enumerableSource = (EnumerablePropertySource<?>) source; EnumerablePropertySource<?> enumerableSource = (EnumerablePropertySource<?>) source;
for (String name : enumerableSource.getPropertyNames()) { for (String name : enumerableSource.getPropertyNames()) {
System.out.println(name + " = " + enumerableSource.getProperty(name)); System.out.println(name + " = " + enumerableSource.getProperty(name));
}
} }
} }
@Test
public void loadSpecificProfile() throws Exception {
StringBuilder yaml = new StringBuilder();
yaml.append("foo:\n bar: spam\n");
yaml.append("---\n");
yaml.append("spring:\n profiles: foo\n");
yaml.append("foo:\n bar: wham\n");
ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes());
PropertySource<?> source = this.loader.load("resource", resource, "foo",
(profile) -> true);
assertThat(source).isNotNull();
assertThat(source.getProperty("foo.bar")).isEqualTo("wham");
}
@Test
public void loadWithAcceptProfile() throws Exception {
StringBuilder yaml = new StringBuilder();
yaml.append("---\n");
yaml.append("spring:\n profiles: yay,foo\n");
yaml.append("foo:\n bar: bang\n");
yaml.append("---\n");
yaml.append("spring:\n profiles: yay,!foo\n");
yaml.append("foo:\n bar: wham\n");
ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes());
PropertySource<?> source = this.loader.load("resource", resource, "yay",
(profiles) -> ObjectUtils.containsElement(profiles, "!foo"));
assertThat(source).isNotNull();
assertThat(source.getProperty("foo.bar")).isEqualTo("wham");
}
} }
spring:
profiles:
active:
- A
- B
---
spring.profiles: A
spring:
profiles:
include:
- C
- E
---
spring.profiles: B
spring:
profiles:
include:
- D
- E
---
spring.profiles: E
version: E
---
spring.profiles: "!A"
not-a: true
---
spring.profiles: "!B"
not-b: true
---
spring.profiles: "!C"
not-c: true
---
spring.profiles: "!D"
not-d: true
---
spring.profiles: "!E"
not-e: true
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment