Introduce SystemEnvironmentPropertySource
Properties such as 'spring.profiles.active' cannot be specified at the command line under Bash and other shells due to variable naming constraints. This change allows for exchanging underscores for periods as well as capitalizing property names for more idiomatic naming when dealing with environment variables. For example, Spring will respect equally either of the following: spring.profiles.active=p1 java -classpath ... MyApp SPRING_PROFILES_ACTIVE=p1 java -classpath ... MyApp The former is not possible under Bash, while the latter is. No code or configuration changes are required; SystemEnvironmentPropertySource adapts for these varations automatically. SystemEnvironmentPropertySource is added by default as "systemEnvironment" to StandardEnvironment and all subtypes, taking the place of the plain MapPropertySource that was in use before this change. Issue: SPR-8869
This commit is contained in:
parent
2c26a23c46
commit
143db0d8de
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.core.env;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
@ -44,6 +46,8 @@ public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
|
|||
|
||||
protected static final String[] EMPTY_NAMES_ARRAY = new String[0];
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
||||
public EnumerablePropertySource(String name, T source) {
|
||||
super(name, source);
|
||||
|
@ -65,9 +69,15 @@ public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
|
|||
Assert.notNull(name, "property name must not be null");
|
||||
for (String candidate : this.getPropertyNames()) {
|
||||
if (candidate.equals(name)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("PropertySource [%s] contains '%s'", getName(), name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("PropertySource [%s] does not contain '%s'", getName(), name));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ package org.springframework.core.env;
|
|||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see ConfigurableEnvironment
|
||||
* @see SystemEnvironmentPropertySource
|
||||
* @see org.springframework.web.context.support.StandardServletEnvironment
|
||||
*/
|
||||
public class StandardEnvironment extends AbstractEnvironment {
|
||||
|
@ -71,7 +72,7 @@ public class StandardEnvironment extends AbstractEnvironment {
|
|||
@Override
|
||||
protected void customizePropertySources(MutablePropertySources propertySources) {
|
||||
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
|
||||
propertySources.addLast(new MapPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
|
||||
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.core.env;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Specialization of {@link MapPropertySource} designed for use with
|
||||
* {@linkplain AbstractEnvironment#getSystemEnvironment() system environment variables}.
|
||||
* Compensates for constraints in Bash and other shells that do not allow for variables
|
||||
* containing the period character; also allows for uppercase variations on property
|
||||
* names for more idiomatic shell use.
|
||||
*
|
||||
* <p>For example, a call to {@code getProperty("foo.bar")} will attempt to find a value
|
||||
* for the original property or any 'equivalent' property, returning the first found:
|
||||
* <ul>
|
||||
* <li>{@code foo.bar} - the original name</li>
|
||||
* <li>{@code foo_bar} - with underscores for periods (if any)</li>
|
||||
* <li>{@code FOO.BAR} - original, with upper case</li>
|
||||
* <li>{@code FOO_BAR} - with underscores and upper case</li>
|
||||
* </ul>
|
||||
*
|
||||
* The same applies for calls to {@link #containsProperty(String)}, which returns
|
||||
* {@code true} if any of the above properties are present, otherwise {@code false}.
|
||||
*
|
||||
* <p>This feature is particularly useful when specifying active or default profiles as
|
||||
* environment variables. The following is not allowable under Bash
|
||||
*
|
||||
* <pre class="code">spring.profiles.active=p1 java -classpath ... MyApp</pre>
|
||||
*
|
||||
* However, the following syntax is permitted and is also more conventional.
|
||||
*
|
||||
* <pre class="code">SPRING_PROFILES_ACTIVE=p1 java -classpath ... MyApp</pre>
|
||||
*
|
||||
* <p>Enable debug- or trace-level logging for this class (or package) for messages
|
||||
* explaining when these 'property name resolutions' occur.
|
||||
*
|
||||
* <p>This property source is included by default in {@link StandardEnvironment} and all
|
||||
* its subclasses.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see StandardEnvironment
|
||||
* @see AbstractEnvironment#getSystemEnvironment()
|
||||
* @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
|
||||
*/
|
||||
public class SystemEnvironmentPropertySource extends MapPropertySource {
|
||||
|
||||
/**
|
||||
* Create a new {@code SystemEnvironmentPropertySource} with the given name and
|
||||
* delegating to the given {@code MapPropertySource}.
|
||||
*/
|
||||
public SystemEnvironmentPropertySource(String name, Map<String, Object> source) {
|
||||
super(name, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a property with the given name or any underscore/uppercase variant
|
||||
* thereof exists in this property source.
|
||||
*/
|
||||
@Override
|
||||
public boolean containsProperty(String name) {
|
||||
return resolvePropertyName(name) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation returns {@code true} if a property with the given name or
|
||||
* any underscore/uppercase variant thereof exists in this property source.
|
||||
*/
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
Assert.notNull(name, "property name must not be null");
|
||||
String actualName = resolvePropertyName(name);
|
||||
if (actualName == null) {
|
||||
// at this point we know the property does not exist
|
||||
return null;
|
||||
}
|
||||
if (logger.isDebugEnabled() && !name.equals(actualName)) {
|
||||
logger.debug(String.format(
|
||||
"PropertySource [%s] does not contain '%s', but found equivalent '%s'",
|
||||
this.getName(), name, actualName));
|
||||
}
|
||||
return super.getProperty(actualName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this property source contains a property with the given name, or
|
||||
* any underscore / uppercase variation thereof. Return the resolved name or
|
||||
* {@code null} if none found.
|
||||
*/
|
||||
private String resolvePropertyName(String name) {
|
||||
if (super.containsProperty(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
String usName = name.replace('.', '_');
|
||||
if (!name.equals(usName) && super.containsProperty(usName)) {
|
||||
return usName;
|
||||
}
|
||||
|
||||
String ucName = name.toUpperCase();
|
||||
if (!name.equals(ucName)) {
|
||||
if (super.containsProperty(ucName)) {
|
||||
return ucName;
|
||||
} else {
|
||||
String usUcName = ucName.replace('.', '_');
|
||||
if (!ucName.equals(usUcName) && super.containsProperty(usUcName)) {
|
||||
return usUcName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -71,6 +71,13 @@ public class StandardEnvironmentTests {
|
|||
assertThat(sources.size(), is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertySourceTypes() {
|
||||
ConfigurableEnvironment env = new StandardEnvironment();
|
||||
MutablePropertySources sources = env.getPropertySources();
|
||||
assertThat(sources.get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME), instanceOf(SystemEnvironmentPropertySource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void activeProfilesIsEmptyByDefault() {
|
||||
assertThat(environment.getActiveProfiles().length, is(0));
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2002-2011 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.core.env;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SystemEnvironmentPropertySource}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class SystemEnvironmentPropertySourceTests {
|
||||
|
||||
private Map<String, Object> envMap;
|
||||
private PropertySource<?> ps;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
envMap = new HashMap<String, Object>();
|
||||
ps = new SystemEnvironmentPropertySource("sysEnv", envMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void none() {
|
||||
//envMap.put("a.key", "a_value");
|
||||
|
||||
assertThat(ps.containsProperty("a.key"), equalTo(false));
|
||||
assertThat(ps.getProperty("a.key"), equalTo(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalWithoutPeriod() {
|
||||
envMap.put("akey", "avalue");
|
||||
|
||||
assertThat(ps.containsProperty("akey"), equalTo(true));
|
||||
assertThat(ps.getProperty("akey"), equalTo((Object)"avalue"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalWithPeriod() {
|
||||
envMap.put("a.key", "a.value");
|
||||
|
||||
assertThat(ps.containsProperty("a.key"), equalTo(true));
|
||||
assertThat(ps.getProperty("a.key"), equalTo((Object)"a.value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withUnderscore() {
|
||||
envMap.put("a_key", "a_value");
|
||||
|
||||
assertThat(ps.containsProperty("a_key"), equalTo(true));
|
||||
assertThat(ps.containsProperty("a.key"), equalTo(true));
|
||||
|
||||
assertThat(ps.getProperty("a_key"), equalTo((Object)"a_value"));
|
||||
assertThat( ps.getProperty("a.key"), equalTo((Object)"a_value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withBothPeriodAndUnderscore() {
|
||||
envMap.put("a_key", "a_value");
|
||||
envMap.put("a.key", "a.value");
|
||||
|
||||
assertThat(ps.getProperty("a_key"), equalTo((Object)"a_value"));
|
||||
assertThat( ps.getProperty("a.key"), equalTo((Object)"a.value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withUppercase() {
|
||||
envMap.put("A_KEY", "a_value");
|
||||
|
||||
assertThat(ps.containsProperty("A_KEY"), equalTo(true));
|
||||
assertThat(ps.containsProperty("A.KEY"), equalTo(true));
|
||||
assertThat(ps.containsProperty("a_key"), equalTo(true));
|
||||
assertThat(ps.containsProperty("a.key"), equalTo(true));
|
||||
|
||||
assertThat(ps.getProperty("A_KEY"), equalTo((Object)"a_value"));
|
||||
assertThat(ps.getProperty("A.KEY"), equalTo((Object)"a_value"));
|
||||
assertThat(ps.getProperty("a_key"), equalTo((Object)"a_value"));
|
||||
assertThat(ps.getProperty("a.key"), equalTo((Object)"a_value"));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue