Merge branch '1.5.x'

pull/7590/head
Andy Wilkinson 8 years ago
commit 4c7a8825b1

@ -16,16 +16,25 @@
package org.springframework.boot.yaml; package org.springframework.boot.yaml;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher; import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher;
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus; import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
import org.springframework.boot.bind.PropertySourcesPropertyValues;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -38,11 +47,10 @@ import org.springframework.util.StringUtils;
* @author Dave Syer * @author Dave Syer
* @author Matt Benson * @author Matt Benson
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson
*/ */
public class SpringProfileDocumentMatcher implements DocumentMatcher { public class SpringProfileDocumentMatcher implements DocumentMatcher {
private static final String SPRING_PROFILES = "spring.profiles";
private String[] activeProfiles = new String[0]; private String[] activeProfiles = new String[0];
public SpringProfileDocumentMatcher() { public SpringProfileDocumentMatcher() {
@ -61,46 +69,54 @@ public class SpringProfileDocumentMatcher implements DocumentMatcher {
@Override @Override
public MatchStatus matches(Properties properties) { public MatchStatus matches(Properties properties) {
DocumentMatcher activeProfilesMatcher = getActiveProfilesDocumentMatcher(); List<String> profiles = extractSpringProfiles(properties);
String profiles = properties.getProperty(SPRING_PROFILES); ProfilesMatcher profilesMatcher = getProfilesMatcher();
String negative = extractProfiles(profiles, ProfileType.NEGATIVE); Set<String> negative = extractProfiles(profiles, ProfileType.NEGATIVE);
String positive = extractProfiles(profiles, ProfileType.POSITIVE); Set<String> positive = extractProfiles(profiles, ProfileType.POSITIVE);
if (StringUtils.hasLength(negative)) { if (!CollectionUtils.isEmpty(negative)) {
properties = new Properties(properties); if (profilesMatcher.matches(negative) == MatchStatus.FOUND) {
properties.setProperty(SPRING_PROFILES, negative);
if (activeProfilesMatcher.matches(properties) == MatchStatus.FOUND) {
return MatchStatus.NOT_FOUND; return MatchStatus.NOT_FOUND;
} }
if (StringUtils.isEmpty(positive)) { if (CollectionUtils.isEmpty(positive)) {
return MatchStatus.FOUND; return MatchStatus.FOUND;
} }
properties.setProperty(SPRING_PROFILES, positive);
} }
return activeProfilesMatcher.matches(properties); return profilesMatcher.matches(positive);
}
private List<String> extractSpringProfiles(Properties properties) {
SpringProperties springProperties = new SpringProperties();
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addFirst(new PropertiesPropertySource("profiles", properties));
PropertyValues propertyValues = new PropertySourcesPropertyValues(
propertySources);
new RelaxedDataBinder(springProperties, "spring").bind(propertyValues);
List<String> profiles = springProperties.getProfiles();
return profiles;
} }
private DocumentMatcher getActiveProfilesDocumentMatcher() { private ProfilesMatcher getProfilesMatcher() {
return this.activeProfiles.length == 0 ? new EmptyProfileDocumentMatcher() return this.activeProfiles.length == 0 ? new EmptyProfilesMatcher()
: new ActiveProfilesDocumentMatcher( : new ActiveProfilesMatcher(
new HashSet<String>(Arrays.asList(this.activeProfiles))); new HashSet<String>(Arrays.asList(this.activeProfiles)));
} }
private String extractProfiles(String profiles, ProfileType type) { private Set<String> extractProfiles(List<String> profiles, ProfileType type) {
if (profiles == null) { if (CollectionUtils.isEmpty(profiles)) {
return null; return null;
} }
StringBuilder result = new StringBuilder(); Set<String> extractedProfiles = new HashSet<String>();
for (String candidate : StringUtils.commaDelimitedListToSet(profiles)) { for (String candidate : profiles) {
ProfileType candidateType = ProfileType.POSITIVE; ProfileType candidateType = ProfileType.POSITIVE;
if (candidate.startsWith("!")) { if (candidate.startsWith("!")) {
candidateType = ProfileType.NEGATIVE; candidateType = ProfileType.NEGATIVE;
} }
if (candidateType == type) { if (candidateType == type) {
result.append(result.length() > 0 ? "," : ""); extractedProfiles.add(type == ProfileType.POSITIVE ? candidate
result.append(candidate.substring(type == ProfileType.POSITIVE ? 0 : 1)); : candidate.substring(1));
} }
} }
return result.toString(); return extractedProfiles;
} }
/** /**
@ -111,40 +127,35 @@ public class SpringProfileDocumentMatcher implements DocumentMatcher {
} }
/** /**
* Base class for profile-based {@link DocumentMatcher DocumentMatchers}. * Base class for profile matchers.
*/ */
private static abstract class AbstractProfileDocumentMatcher private static abstract class ProfilesMatcher {
implements DocumentMatcher {
@Override public final MatchStatus matches(Set<String> profiles) {
public final MatchStatus matches(Properties properties) { if (CollectionUtils.isEmpty(profiles)) {
if (!properties.containsKey(SPRING_PROFILES)) {
return MatchStatus.ABSTAIN; return MatchStatus.ABSTAIN;
} }
Set<String> profiles = StringUtils return doMatches(profiles);
.commaDelimitedListToSet(properties.getProperty(SPRING_PROFILES));
return matches(profiles);
} }
protected abstract MatchStatus matches(Set<String> profiles); protected abstract MatchStatus doMatches(Set<String> profiles);
} }
/** /**
* {@link AbstractProfileDocumentMatcher} that matches a document when a value in * {@link ProfileMatcher} that matches when a value in {@code spring.profiles} is also
* {@code spring.profiles} is also in {@code spring.profiles.active}. * in {@code spring.profiles.active}.
*/ */
private static class ActiveProfilesDocumentMatcher private static class ActiveProfilesMatcher extends ProfilesMatcher {
extends AbstractProfileDocumentMatcher {
private final Set<String> activeProfiles; private final Set<String> activeProfiles;
ActiveProfilesDocumentMatcher(Set<String> activeProfiles) { ActiveProfilesMatcher(Set<String> activeProfiles) {
this.activeProfiles = activeProfiles; this.activeProfiles = activeProfiles;
} }
@Override @Override
protected MatchStatus matches(Set<String> profiles) { protected MatchStatus doMatches(Set<String> profiles) {
if (profiles.isEmpty()) { if (profiles.isEmpty()) {
return MatchStatus.NOT_FOUND; return MatchStatus.NOT_FOUND;
} }
@ -159,20 +170,19 @@ public class SpringProfileDocumentMatcher implements DocumentMatcher {
} }
/** /**
* {@link AbstractProfileDocumentMatcher} that matches a document when {@code * {@link ProfilesMatcher} that matches when {@code
* spring.profiles} is empty or contains a value with no text. * spring.profiles} is empty or contains a value with no text.
* *
* @see StringUtils#hasText(String) * @see StringUtils#hasText(String)
*/ */
private static class EmptyProfileDocumentMatcher private static class EmptyProfilesMatcher extends ProfilesMatcher {
extends AbstractProfileDocumentMatcher {
@Override @Override
public MatchStatus matches(Set<String> profiles) { public MatchStatus doMatches(Set<String> springProfiles) {
if (profiles.isEmpty()) { if (springProfiles.isEmpty()) {
return MatchStatus.FOUND; return MatchStatus.FOUND;
} }
for (String profile : profiles) { for (String profile : springProfiles) {
if (!StringUtils.hasText(profile)) { if (!StringUtils.hasText(profile)) {
return MatchStatus.FOUND; return MatchStatus.FOUND;
} }
@ -182,4 +192,22 @@ public class SpringProfileDocumentMatcher implements DocumentMatcher {
} }
/**
* Class for binding {@code spring.profiles} property.
*/
@ConfigurationProperties("spring")
static class SpringProperties {
private List<String> profiles = new ArrayList<String>();
public List<String> getProfiles() {
return this.profiles;
}
public void setProfiles(List<String> profiles) {
this.profiles = profiles;
}
}
} }

@ -23,8 +23,8 @@ import org.junit.Test;
import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher; import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher;
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus; import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -32,6 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link SpringProfileDocumentMatcher}. * Tests for {@link SpringProfileDocumentMatcher}.
* *
* @author Matt Benson * @author Matt Benson
* @author Andy Wilkinson
*/ */
public class SpringProfileDocumentMatcherTests { public class SpringProfileDocumentMatcherTests {
@ -57,12 +58,28 @@ public class SpringProfileDocumentMatcherTests {
} }
@Test @Test
public void matchesCommaSeparatedArray() throws IOException { public void matchesCommaSeparatedString() throws IOException {
DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar");
Properties properties = getProperties("spring.profiles: bar,spam"); Properties properties = getProperties("spring.profiles: bar,spam");
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND);
} }
@Test
public void matchesCommaSeparatedArray() throws IOException {
DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar");
Properties properties = getProperties(
String.format("spring.profiles: [bar, spam]"));
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND);
}
@Test
public void matchesList() throws IOException {
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 @Test
public void noMatchingProfiles() throws IOException { public void noMatchingProfiles() throws IOException {
DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar");
@ -73,41 +90,44 @@ public class SpringProfileDocumentMatcherTests {
@Test @Test
public void inverseMatchSingle() throws IOException { public void inverseMatchSingle() throws IOException {
DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar");
Properties properties = getProperties("spring.profiles: !baz"); Properties properties = getProperties("spring.profiles: '!baz'");
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND);
} }
@Test @Test
public void testInverseMatchMulti() throws IOException { public void testInverseMatchMulti() throws IOException {
DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar");
Properties properties = getProperties("spring.profiles: !baz,!blah"); Properties properties = getProperties("spring.profiles: '!baz,!blah'");
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND);
} }
@Test @Test
public void negatedWithMatch() throws Exception { public void negatedWithMatch() throws Exception {
DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar", "blah"); DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar", "blah");
Properties properties = getProperties("spring.profiles: !baz,blah"); Properties properties = getProperties("spring.profiles: '!baz,blah'");
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND);
} }
@Test @Test
public void negatedWithNoMatch() throws IOException { public void negatedWithNoMatch() throws IOException {
DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar", "blah"); DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar", "blah");
Properties properties = getProperties("spring.profiles: !baz,another"); Properties properties = getProperties("spring.profiles: '!baz,another'");
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND); assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
} }
@Test @Test
public void negatedTrumpsMatching() throws IOException { public void negatedTrumpsMatching() throws IOException {
DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "baz", "blah"); DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "baz", "blah");
Properties properties = getProperties("spring.profiles: !baz,blah"); Properties properties = getProperties("spring.profiles: '!baz,blah'");
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND); assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
} }
private Properties getProperties(String values) throws IOException { private Properties getProperties(String values) throws IOException {
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
ByteArrayResource resource = new ByteArrayResource(values.getBytes()); ByteArrayResource resource = new ByteArrayResource(values.getBytes());
return PropertiesLoaderUtils.loadProperties(resource); yamlPropertiesFactoryBean.setResources(resource);
yamlPropertiesFactoryBean.afterPropertiesSet();
return yamlPropertiesFactoryBean.getObject();
} }
} }

Loading…
Cancel
Save