org.springframework.core.env.SystemEnvironmentPropertySource Maven / Gradle / Ivy
/*
* Copyright 2002-2021 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
*
* https://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.lang.Nullable;
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 and/or hyphen character; also allows for uppercase
* variations on property names for more idiomatic shell use.
*
* 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:
*
* - {@code foo.bar} - the original name
* - {@code foo_bar} - with underscores for periods (if any)
* - {@code FOO.BAR} - original, with upper case
* - {@code FOO_BAR} - with underscores and upper case
*
* Any hyphen variant of the above would work as well, or even mix dot/hyphen variants.
*
* The same applies for calls to {@link #containsProperty(String)}, which returns
* {@code true} if any of the above properties are present, otherwise {@code false}.
*
*
This feature is particularly useful when specifying active or default profiles as
* environment variables. The following is not allowable under Bash:
*
*
spring.profiles.active=p1 java -classpath ... MyApp
*
* However, the following syntax is permitted and is also more conventional:
*
* SPRING_PROFILES_ACTIVE=p1 java -classpath ... MyApp
*
* Enable debug- or trace-level logging for this class (or package) for messages
* explaining when these 'property name resolutions' occur.
*
*
This property source is included by default in {@link StandardEnvironment}
* and all its subclasses.
*
* @author Chris Beams
* @author Juergen Hoeller
* @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 source) {
super(name, source);
}
/**
* Return {@code 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 (getProperty(name) != null);
}
/**
* This implementation returns {@code true} if a property with the given name or
* any underscore/uppercase variant thereof exists in this property source.
*/
@Override
@Nullable
public Object getProperty(String name) {
String actualName = resolvePropertyName(name);
if (logger.isDebugEnabled() && !name.equals(actualName)) {
logger.debug("PropertySource '" + getName() + "' does not contain property '" + name +
"', but found equivalent '" + 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 if one is
* found or otherwise the original name. Never returns {@code null}.
*/
protected final String resolvePropertyName(String name) {
Assert.notNull(name, "Property name must not be null");
String resolvedName = checkPropertyName(name);
if (resolvedName != null) {
return resolvedName;
}
String uppercasedName = name.toUpperCase();
if (!name.equals(uppercasedName)) {
resolvedName = checkPropertyName(uppercasedName);
if (resolvedName != null) {
return resolvedName;
}
}
return name;
}
@Nullable
private String checkPropertyName(String name) {
// Check name as-is
if (this.source.containsKey(name)) {
return name;
}
// Check name with just dots replaced
String noDotName = name.replace('.', '_');
if (!name.equals(noDotName) && this.source.containsKey(noDotName)) {
return noDotName;
}
// Check name with just hyphens replaced
String noHyphenName = name.replace('-', '_');
if (!name.equals(noHyphenName) && this.source.containsKey(noHyphenName)) {
return noHyphenName;
}
// Check name with dots and hyphens replaced
String noDotNoHyphenName = noDotName.replace('-', '_');
if (!noDotName.equals(noDotNoHyphenName) && this.source.containsKey(noDotNoHyphenName)) {
return noDotNoHyphenName;
}
// Give up
return null;
}
}