From 4bde6b80eed3def41b616f39c94496a9b5a0a405 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 20 Feb 2018 22:01:31 -0800 Subject: [PATCH] Refactor YAML profile to deal with "!" profiles Drop `SpringProfileDocumentMatcher` and replace it with two new implementations that restrict when YAML documents are loaded. YAML sections are now restricted both on the specific profile that is being loaded, and the profiles that are currently accepted. The `PropertySourceLoader` interface has been refined to include a predicate that determines when a profile is accepted. The `ConfigFileApplicationListener` simply delegates the predicate logic to the `Environment`. Fixes gh-8011 --- .../EnvironmentPostProcessorExample.java | 8 +- .../PropertiesMigrationReporterTests.java | 2 +- .../config/ConfigFileApplicationListener.java | 3 +- .../env/AcceptsProfilesDocumentMatcher.java | 41 ++++ .../boot/env/OriginTrackedYamlLoader.java | 49 +---- .../env/ProfileToLoadDocumentMatcher.java | 49 +++++ .../env/PropertiesPropertySourceLoader.java | 9 +- .../boot/env/PropertySourceLoader.java | 14 +- .../env/SpringProfilesDocumentMatcher.java | 77 +++++++ .../boot/env/YamlPropertySourceLoader.java | 11 +- .../yaml/SpringProfileDocumentMatcher.java | 207 ------------------ .../boot/yaml/package-info.java | 23 -- .../ConfigFileApplicationListenerTests.java | 25 ++- .../AcceptsProfilesDocumentMatcherTests.java | 72 ++++++ .../NoSnakeYamlPropertySourceLoaderTests.java | 4 +- .../env/OriginTrackedYamlLoaderTests.java | 7 +- .../ProfileToLoadDocumentMatcherTests.java | 80 +++++++ .../PropertiesPropertySourceLoaderTests.java | 8 +- .../SpringProfilesDocumentMatcherTests.java | 93 ++++++++ .../env/YamlPropertySourceLoaderTests.java | 47 +++- .../SpringProfileDocumentMatcherTests.java | 131 ----------- .../test/resources/testnegatedprofiles.yml | 20 ++ 22 files changed, 542 insertions(+), 438 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/AcceptsProfilesDocumentMatcher.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/ProfileToLoadDocumentMatcher.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/SpringProfilesDocumentMatcher.java delete mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java delete mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/yaml/package-info.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/AcceptsProfilesDocumentMatcherTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/ProfileToLoadDocumentMatcherTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/SpringProfilesDocumentMatcherTests.java delete mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java create mode 100644 spring-boot-project/spring-boot/src/test/resources/testnegatedprofiles.yml diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/EnvironmentPostProcessorExample.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/EnvironmentPostProcessorExample.java index 210b9b32a8a..994ad98eaf4 100644 --- a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/EnvironmentPostProcessorExample.java +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/EnvironmentPostProcessorExample.java @@ -22,6 +22,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; @@ -40,16 +41,17 @@ public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { Resource path = new ClassPathResource("com/example/myapp/config.yml"); - PropertySource propertySource = loadYaml(path); + PropertySource propertySource = loadYaml(path, environment); environment.getPropertySources().addLast(propertySource); } - private PropertySource loadYaml(Resource path) { + private PropertySource loadYaml(Resource path, Environment environment) { if (!path.exists()) { throw new IllegalArgumentException("Resource " + path + " does not exist"); } try { - return this.loader.load("custom-resource", path, null); + return this.loader.load("custom-resource", path, null, + environment::acceptsProfiles); } catch (IOException ex) { throw new IllegalStateException( diff --git a/spring-boot-project/spring-boot-properties-migrator/src/test/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporterTests.java b/spring-boot-project/spring-boot-properties-migrator/src/test/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporterTests.java index 0ccc88ec74a..2df3744e5ab 100644 --- a/spring-boot-project/spring-boot-properties-migrator/src/test/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporterTests.java +++ b/spring-boot-project/spring-boot-properties-migrator/src/test/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporterTests.java @@ -181,7 +181,7 @@ public class PropertiesMigrationReporterTests { throws IOException { ClassPathResource resource = new ClassPathResource(path); PropertySource propertySource = new PropertiesPropertySourceLoader().load(name, - resource, null); + resource, null, (profile) -> true); assertThat(propertySource).isNotNull(); return propertySource; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java index 61b8d5dffb8..140043333b2 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java @@ -466,7 +466,8 @@ public class ConfigFileApplicationListener } String name = "applicationConfig: [" + location + "]" + (loadProfile == null ? "" : "#" + loadProfile); - PropertySource loaded = loader.load(name, resource, loadProfile); + PropertySource loaded = loader.load(name, resource, loadProfile, + this.environment::acceptsProfiles); if (loaded == null) { this.logger.trace("Skipped unloaded config " + description); return; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/AcceptsProfilesDocumentMatcher.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/AcceptsProfilesDocumentMatcher.java new file mode 100644 index 00000000000..6d2713b926c --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/AcceptsProfilesDocumentMatcher.java @@ -0,0 +1,41 @@ +/* + * 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 acceptsProfiles; + + AcceptsProfilesDocumentMatcher(Predicate acceptsProfiles) { + this.acceptsProfiles = acceptsProfiles; + } + + @Override + protected boolean matches(String[] profiles) { + return ObjectUtils.isEmpty(profiles) || this.acceptsProfiles.test(profiles); + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java index c38030c1b86..c679d5c343d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -17,9 +17,8 @@ package org.springframework.boot.env; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -import java.util.Properties; +import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -41,11 +40,10 @@ import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.OriginTrackedValue; import org.springframework.boot.origin.TextResourceOrigin; import org.springframework.boot.origin.TextResourceOrigin.Location; -import org.springframework.boot.yaml.SpringProfileDocumentMatcher; import org.springframework.core.io.Resource; /** - * Class to load {@code .yml} files into a map of {@code String} -> + * Class to load {@code .yml} files into a map of {@code String} to * {@link OriginTrackedValue}. * * @author Madhura Bhave @@ -55,16 +53,11 @@ class OriginTrackedYamlLoader extends YamlProcessor { private final Resource resource; - OriginTrackedYamlLoader(Resource resource, String profile) { + OriginTrackedYamlLoader(Resource resource, String profileToLoad, + Predicate acceptsProfiles) { this.resource = resource; - if (profile == null) { - setMatchDefault(true); - setDocumentMatchers(new OriginTrackedSpringProfileDocumentMatcher()); - } - else { - setMatchDefault(false); - setDocumentMatchers(new OriginTrackedSpringProfileDocumentMatcher(profile)); - } + setDocumentMatchers(new ProfileToLoadDocumentMatcher(profileToLoad), + new AcceptsProfilesDocumentMatcher(acceptsProfiles)); setResources(resource); } @@ -164,32 +157,4 @@ class OriginTrackedYamlLoader extends YamlProcessor { } - /** - * {@link SpringProfileDocumentMatcher} that deals with {@link OriginTrackedValue - * OriginTrackedValues}. - */ - private static class OriginTrackedSpringProfileDocumentMatcher - extends SpringProfileDocumentMatcher { - - OriginTrackedSpringProfileDocumentMatcher(String... profiles) { - super(profiles); - } - - @Override - protected List extractSpringProfiles(Properties properties) { - Properties springProperties = new Properties(); - for (Map.Entry entry : properties.entrySet()) { - if (String.valueOf(entry.getKey()).startsWith("spring.")) { - Object value = entry.getValue(); - if (value instanceof OriginTrackedValue) { - value = ((OriginTrackedValue) value).getValue(); - } - springProperties.put(entry.getKey(), value); - } - } - return super.extractSpringProfiles(springProperties); - } - - } - } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/ProfileToLoadDocumentMatcher.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/ProfileToLoadDocumentMatcher.java new file mode 100644 index 00000000000..48cbf6208b5 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/ProfileToLoadDocumentMatcher.java @@ -0,0 +1,49 @@ +/* + * 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::isPositveProfile).toArray(String[]::new)); + if (this.profile == null) { + return ObjectUtils.isEmpty(positiveProfiles); + } + return ObjectUtils.containsElement(positiveProfiles, this.profile); + } + + private boolean isPositveProfile(String profile) { + return !profile.startsWith("!"); + } +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/PropertiesPropertySourceLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/PropertiesPropertySourceLoader.java index 847077dbfef..3af328f8f3e 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/PropertiesPropertySourceLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/PropertiesPropertySourceLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -18,6 +18,7 @@ package org.springframework.boot.env; import java.io.IOException; import java.util.Map; +import java.util.function.Predicate; import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; @@ -40,9 +41,9 @@ public class PropertiesPropertySourceLoader implements PropertySourceLoader { } @Override - public PropertySource load(String name, Resource resource, String profile) - throws IOException { - if (profile == null) { + public PropertySource load(String name, Resource resource, String profileToLoad, + Predicate acceptsProfiles) throws IOException { + if (profileToLoad == null) { Map properties = loadProperties(resource); if (!properties.isEmpty()) { return new OriginTrackedMapPropertySource(name, properties); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/PropertySourceLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/PropertySourceLoader.java index 2d7e3189d2b..8b912318e8c 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/PropertySourceLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/PropertySourceLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -17,6 +17,7 @@ package org.springframework.boot.env; import java.io.IOException; +import java.util.function.Predicate; import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; @@ -41,13 +42,14 @@ public interface PropertySourceLoader { * Load the resource into a property source. * @param name the name of the property source * @param resource the resource to load - * @param profile the name of the profile to load or {@code null}. The profile can be - * used to load multi-document files (such as YAML). Simple property formats should - * {@code null} when asked to load a profile. + * @param profileToLoad the name of the profile to load or {@code null}. The profile + * 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 */ - PropertySource load(String name, Resource resource, String profile) - throws IOException; + PropertySource load(String name, Resource resource, String profileToLoad, + Predicate acceptsProfiles) throws IOException; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/SpringProfilesDocumentMatcher.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/SpringProfilesDocumentMatcher.java new file mode 100644 index 00000000000..e70f4fd1a75 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/SpringProfilesDocumentMatcher.java @@ -0,0 +1,77 @@ +/* + * 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; + } + + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/YamlPropertySourceLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/YamlPropertySourceLoader.java index df85eeed89c..7c5c201a269 100755 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/YamlPropertySourceLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/YamlPropertySourceLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -18,6 +18,7 @@ package org.springframework.boot.env; import java.io.IOException; import java.util.Map; +import java.util.function.Predicate; import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; @@ -38,14 +39,14 @@ public class YamlPropertySourceLoader implements PropertySourceLoader { } @Override - public PropertySource load(String name, Resource resource, String profile) - throws IOException { + public PropertySource load(String name, Resource resource, String profileToLoad, + Predicate acceptsProfiles) throws IOException { if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) { throw new IllegalStateException("Attempted to load " + name + " but snakeyaml was not found on the classpath"); } - Map source = new OriginTrackedYamlLoader(resource, profile) - .load(); + Map source = new OriginTrackedYamlLoader(resource, profileToLoad, + acceptsProfiles).load(); if (!source.isEmpty()) { return new OriginTrackedMapPropertySource(name, source); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java deleted file mode 100644 index c4c203a56aa..00000000000 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.yaml; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Properties; -import java.util.Set; - -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.MapConfigurationPropertySource; -import org.springframework.core.env.Environment; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * {@link DocumentMatcher} backed by {@link Environment#getActiveProfiles()}. A YAML - * document may define a "spring.profiles" element as a comma-separated list of Spring - * profile names, optionally negated using the {@code !} character. If both negated and - * non-negated profiles are specified for a single document, at least one non-negated - * profile must match and no negated profiles may match. - * - * @author Dave Syer - * @author Matt Benson - * @author Phillip Webb - * @author Andy Wilkinson - * @author Madhura Bhave - */ -public class SpringProfileDocumentMatcher implements DocumentMatcher { - - private String[] activeProfiles = new String[0]; - - public SpringProfileDocumentMatcher(String... profiles) { - addActiveProfiles(profiles); - } - - public void addActiveProfiles(String... profiles) { - LinkedHashSet set = new LinkedHashSet<>( - Arrays.asList(this.activeProfiles)); - Collections.addAll(set, profiles); - this.activeProfiles = set.toArray(new String[set.size()]); - } - - @Override - public MatchStatus matches(Properties properties) { - return matches(extractSpringProfiles(properties)); - } - - protected List extractSpringProfiles(Properties properties) { - Binder binder = new Binder(new MapConfigurationPropertySource(properties)); - return binder.bind("spring.profiles", Bindable.of(String[].class)) - .map(Arrays::asList).orElse(Collections.emptyList()); - } - - private MatchStatus matches(List profiles) { - ProfilesMatcher profilesMatcher = getProfilesMatcher(); - Set negative = extractProfiles(profiles, ProfileType.NEGATIVE); - Set positive = extractProfiles(profiles, ProfileType.POSITIVE); - if (!CollectionUtils.isEmpty(negative)) { - if (profilesMatcher.matches(negative) == MatchStatus.FOUND) { - return MatchStatus.NOT_FOUND; - } - if (CollectionUtils.isEmpty(positive)) { - return MatchStatus.FOUND; - } - } - return profilesMatcher.matches(positive); - } - - private ProfilesMatcher getProfilesMatcher() { - return this.activeProfiles.length == 0 ? new EmptyProfilesMatcher() - : new ActiveProfilesMatcher( - new HashSet<>(Arrays.asList(this.activeProfiles))); - } - - private Set extractProfiles(List profiles, ProfileType type) { - if (CollectionUtils.isEmpty(profiles)) { - return null; - } - Set extractedProfiles = new HashSet<>(); - for (String candidate : profiles) { - ProfileType candidateType = ProfileType.POSITIVE; - if (candidate.startsWith("!")) { - candidateType = ProfileType.NEGATIVE; - } - if (candidateType == type) { - extractedProfiles.add(type == ProfileType.POSITIVE ? candidate - : candidate.substring(1)); - } - } - return extractedProfiles; - } - - /** - * Profile match types. - */ - enum ProfileType { - - POSITIVE, NEGATIVE - - } - - /** - * Base class for profile matchers. - */ - private abstract static class ProfilesMatcher { - - public final MatchStatus matches(Set profiles) { - if (CollectionUtils.isEmpty(profiles)) { - return MatchStatus.ABSTAIN; - } - return doMatches(profiles); - } - - protected abstract MatchStatus doMatches(Set profiles); - - } - - /** - * {@link ProfilesMatcher} that matches when a value in {@code spring.profiles} is - * also in {@code spring.profiles.active}. - */ - private static class ActiveProfilesMatcher extends ProfilesMatcher { - - private final Set activeProfiles; - - ActiveProfilesMatcher(Set activeProfiles) { - this.activeProfiles = activeProfiles; - } - - @Override - protected MatchStatus doMatches(Set profiles) { - if (profiles.isEmpty()) { - return MatchStatus.NOT_FOUND; - } - for (String activeProfile : this.activeProfiles) { - if (profiles.contains(activeProfile)) { - return MatchStatus.FOUND; - } - } - return MatchStatus.NOT_FOUND; - } - - } - - /** - * {@link ProfilesMatcher} that matches when {@code - * spring.profiles} is empty or contains a value with no text. - * - * @see StringUtils#hasText(String) - */ - private static class EmptyProfilesMatcher extends ProfilesMatcher { - - @Override - public MatchStatus doMatches(Set springProfiles) { - if (springProfiles.isEmpty()) { - return MatchStatus.FOUND; - } - for (String profile : springProfiles) { - if (!StringUtils.hasText(profile)) { - return MatchStatus.FOUND; - } - } - return MatchStatus.NOT_FOUND; - } - - } - - /** - * Class for binding {@code spring.profiles} property. - */ - static class SpringProperties { - - private List profiles = new ArrayList<>(); - - public List getProfiles() { - return this.profiles; - } - - public void setProfiles(List profiles) { - this.profiles = profiles; - } - - } - -} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/yaml/package-info.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/yaml/package-info.java deleted file mode 100644 index 1e956c9cddf..00000000000 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/yaml/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2012-2017 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. - */ - -/** - * Spring Boot extensions to Spring Framework's support for parsing YAML. - * - * @see org.springframework.beans.factory.config.YamlPropertiesFactoryBean - * @see org.springframework.beans.factory.config.YamlMapFactoryBean - */ -package org.springframework.boot.yaml; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java index 909315fa4cf..e58daeb8975 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -504,6 +504,29 @@ public class ConfigFileApplicationListenerTests { assertThat(property).isEqualTo("notempty"); } + @Test + public void yamlNegatedProfiles() { + // gh-8011 + this.initializer.setSearchNames("testnegatedprofiles"); + this.initializer.postProcessEnvironment(this.environment, this.application); + String property = this.environment.getProperty("my.property"); + assertThat(property).isEqualTo("fromnototherprofile"); + property = this.environment.getProperty("my.notother"); + assertThat(property).isEqualTo("foo"); + } + + @Test + public void yamlNegatedProfilesWithProfile() { + // gh-8011 + this.initializer.setSearchNames("testnegatedprofiles"); + this.environment.setActiveProfiles("other"); + this.initializer.postProcessEnvironment(this.environment, this.application); + String property = this.environment.getProperty("my.property"); + assertThat(property).isEqualTo("fromotherprofile"); + property = this.environment.getProperty("my.notother"); + assertThat(property).isNull(); + } + @Test public void yamlSetsProfiles() { this.initializer.setSearchNames("testsetprofiles"); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/AcceptsProfilesDocumentMatcherTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/AcceptsProfilesDocumentMatcherTests.java new file mode 100644 index 00000000000..d8c78c1faa1 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/AcceptsProfilesDocumentMatcherTests.java @@ -0,0 +1,72 @@ +/* + * 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 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); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/NoSnakeYamlPropertySourceLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/NoSnakeYamlPropertySourceLoaderTests.java index 3cb0f19c0e1..0b9ef378914 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/NoSnakeYamlPropertySourceLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/NoSnakeYamlPropertySourceLoaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -46,7 +46,7 @@ public class NoSnakeYamlPropertySourceLoaderTests { "Attempted to load resource but snakeyaml was not found on the classpath"); ByteArrayResource resource = new ByteArrayResource( "foo:\n bar: spam".getBytes()); - this.loader.load("resource", resource, null); + this.loader.load("resource", resource, null, (profile) -> true); } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java index d12e4118e8e..d7a30fc3c08 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -43,7 +43,7 @@ public class OriginTrackedYamlLoaderTests { @Before public void setUp() { Resource resource = new ClassPathResource("test-yaml.yml", getClass()); - this.loader = new OriginTrackedYamlLoader(resource, null); + this.loader = new OriginTrackedYamlLoader(resource, null, (profile) -> true); } @Test @@ -93,7 +93,8 @@ public class OriginTrackedYamlLoaderTests { @Test public void processWithActiveProfile() { Resource resource = new ClassPathResource("test-yaml.yml", getClass()); - this.loader = new OriginTrackedYamlLoader(resource, "development"); + this.loader = new OriginTrackedYamlLoader(resource, "development", + (profile) -> true); Map result = this.loader.load(); assertThat(result.get("name").toString()).isEqualTo("Test Name"); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/ProfileToLoadDocumentMatcherTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/ProfileToLoadDocumentMatcherTests.java new file mode 100644 index 00000000000..66f1763c259 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/ProfileToLoadDocumentMatcherTests.java @@ -0,0 +1,80 @@ +/* + * 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); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/PropertiesPropertySourceLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/PropertiesPropertySourceLoaderTests.java index 89440398e46..1617ab77894 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/PropertiesPropertySourceLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/PropertiesPropertySourceLoaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -42,14 +42,16 @@ public class PropertiesPropertySourceLoaderTests { @Test public void loadProperties() throws Exception { PropertySource source = this.loader.load("test.properties", - new ClassPathResource("test-properties.properties", getClass()), null); + new ClassPathResource("test-properties.properties", getClass()), null, + (profile) -> true); assertThat(source.getProperty("test")).isEqualTo("properties"); } @Test public void loadXml() throws Exception { PropertySource source = this.loader.load("test.xml", - new ClassPathResource("test-xml.xml", getClass()), null); + new ClassPathResource("test-xml.xml", getClass()), null, + (profile) -> true); assertThat(source.getProperty("test")).isEqualTo("xml"); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/SpringProfilesDocumentMatcherTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/SpringProfilesDocumentMatcherTests.java new file mode 100644 index 00000000000..b49e099bbcb --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/SpringProfilesDocumentMatcherTests.java @@ -0,0 +1,93 @@ +/* + * 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; + } + + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/YamlPropertySourceLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/YamlPropertySourceLoaderTests.java index b4f6e34012d..d065a99927e 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/YamlPropertySourceLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/YamlPropertySourceLoaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -26,6 +26,7 @@ import org.springframework.core.env.PropertySource; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import org.springframework.util.ObjectUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -44,7 +45,8 @@ public class YamlPropertySourceLoaderTests { public void load() throws Exception { ByteArrayResource resource = new ByteArrayResource( "foo:\n bar: spam".getBytes()); - PropertySource source = this.loader.load("resource", resource, null); + PropertySource source = this.loader.load("resource", resource, null, + (profile) -> true); assertThat(source).isNotNull(); assertThat(source.getProperty("foo.bar")).isEqualTo("spam"); } @@ -59,7 +61,7 @@ public class YamlPropertySourceLoaderTests { } ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes()); EnumerablePropertySource source = (EnumerablePropertySource) this.loader - .load("resource", resource, null); + .load("resource", resource, null, (profile) -> true); assertThat(source).isNotNull(); assertThat(source.getPropertyNames()) .isEqualTo(expected.toArray(new String[] {})); @@ -72,7 +74,8 @@ public class YamlPropertySourceLoaderTests { yaml.append("---\n"); yaml.append("foo:\n baz: wham\n"); ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes()); - PropertySource source = this.loader.load("resource", resource, null); + PropertySource source = this.loader.load("resource", resource, null, + (profile) -> true); assertThat(source).isNotNull(); assertThat(source.getProperty("foo.bar")).isEqualTo("spam"); assertThat(source.getProperty("foo.baz")).isEqualTo("wham"); @@ -81,7 +84,8 @@ public class YamlPropertySourceLoaderTests { @Test public void timestampLikeItemsDoNotBecomeDates() throws Exception { ByteArrayResource resource = new ByteArrayResource("foo: 2015-01-28".getBytes()); - PropertySource source = this.loader.load("resource", resource, null); + PropertySource source = this.loader.load("resource", resource, null, + (profile) -> true); assertThat(source).isNotNull(); assertThat(source.getProperty("foo")).isEqualTo("2015-01-28"); } @@ -89,11 +93,42 @@ public class YamlPropertySourceLoaderTests { @Test public void loadOriginAware() throws Exception { Resource resource = new ClassPathResource("test-yaml.yml", getClass()); - PropertySource source = this.loader.load("resource", resource, null); + PropertySource source = this.loader.load("resource", resource, null, + (profile) -> true); EnumerablePropertySource enumerableSource = (EnumerablePropertySource) source; for (String name : enumerableSource.getPropertyNames()) { 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"); + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java deleted file mode 100644 index 8fbbc19cd68..00000000000 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2012-2017 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.yaml; - -import java.util.Properties; - -import org.junit.Test; - -import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher; -import org.springframework.beans.factory.config.YamlProcessor.MatchStatus; -import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; -import org.springframework.core.io.ByteArrayResource; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link SpringProfileDocumentMatcher}. - * - * @author Matt Benson - * @author Andy Wilkinson - */ -public class SpringProfileDocumentMatcherTests { - - @Test - public void matchesSingleProfile() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); - Properties properties = getProperties("spring.ProfILEs: foo"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); - } - - @Test - public void abstainNoConfiguredProfiles() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); - Properties properties = getProperties("some.property: spam"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN); - } - - @Test - public void noActiveProfiles() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher(); - Properties properties = getProperties("spring.profiles: bar,spam"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND); - } - - @Test - public void matchesCommaSeparatedString() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); - Properties properties = getProperties("spring.profiles: bar,spam"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); - } - - @Test - public void matchesCommaSeparatedArray() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); - Properties properties = getProperties("spring.profiles: [bar, spam]"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); - } - - @Test - public void matchesList() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); - Properties properties = getProperties( - String.format("spring.profiles:%n - bar%n - spam")); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); - } - - @Test - public void noMatchingProfiles() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); - Properties properties = getProperties("spring.profiles: baz,blah"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND); - } - - @Test - public void inverseMatchSingle() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); - Properties properties = getProperties("spring.profiles: '!baz'"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); - } - - @Test - public void testInverseMatchMulti() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); - Properties properties = getProperties("spring.profiles: '!baz,!blah'"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); - } - - @Test - public void negatedWithMatch() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar", "blah"); - Properties properties = getProperties("spring.profiles: '!baz,blah'"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); - } - - @Test - public void negatedWithNoMatch() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar", "blah"); - Properties properties = getProperties("spring.profiles: '!baz,another'"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND); - } - - @Test - public void negatedTrumpsMatching() { - DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "baz", "blah"); - Properties properties = getProperties("spring.profiles: '!baz,blah'"); - assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND); - } - - private Properties getProperties(String values) { - YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean(); - ByteArrayResource resource = new ByteArrayResource(values.getBytes()); - yamlPropertiesFactoryBean.setResources(resource); - yamlPropertiesFactoryBean.afterPropertiesSet(); - return yamlPropertiesFactoryBean.getObject(); - } - -} diff --git a/spring-boot-project/spring-boot/src/test/resources/testnegatedprofiles.yml b/spring-boot-project/spring-boot/src/test/resources/testnegatedprofiles.yml new file mode 100644 index 00000000000..a015a0749e3 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/resources/testnegatedprofiles.yml @@ -0,0 +1,20 @@ +--- +my: + property: fromyamlfile + other: notempty +--- +spring: + profiles: dev +my: + property: fromdevprofile +--- +spring: + profiles: other +my: + property: fromotherprofile +--- +spring: + profiles: "!other" +my: + property: fromnototherprofile + notother: foo \ No newline at end of file