Make SystemEnvironmentPropertySource origin aware

Closes gh-9252
This commit is contained in:
Madhura Bhave 2017-05-26 10:01:34 -07:00
parent d745b69630
commit 6f022565a5
7 changed files with 419 additions and 1 deletions

View File

@ -0,0 +1,63 @@
/*
* 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.env;
import java.util.Map;
import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginLookup;
import org.springframework.boot.origin.OriginTrackedValue;
import org.springframework.core.env.SystemEnvironmentPropertySource;
/**
* {@link SystemEnvironmentPropertySource} and {@link OriginLookup} backed by a map
* containing {@link OriginTrackedValue OriginTrackedValues}.
*
* @author Madhura Bhave
* @since 2.0.0
*/
public class OriginTrackedSystemPropertySource extends SystemEnvironmentPropertySource implements OriginLookup<String> {
/**
* Create a new {@code OriginTrackedSystemPropertySource} with the given name and
* delegating to the given {@code MapPropertySource}.
* @param name the property name
* @param source the underlying map
*/
public OriginTrackedSystemPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
@Override
public Object getProperty(String name) {
Object value = super.getProperty(name);
if (value instanceof OriginTrackedValue) {
return ((OriginTrackedValue) value).getValue();
}
return value;
}
@Override
public Origin getOrigin(String key) {
Object value = super.getProperty(key);
if (value instanceof OriginTrackedValue) {
return ((OriginTrackedValue) value).getOrigin();
}
return null;
}
}

View File

@ -0,0 +1,81 @@
/*
* 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.env;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.origin.OriginTrackedValue;
import org.springframework.boot.origin.SystemEnvironmentOrigin;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.env.SystemEnvironmentPropertySource;
/**
* An {@link EnvironmentPostProcessor} that replaces the systemEnvironment {@link SystemEnvironmentPropertySource}
* with an {@link OriginTrackedSystemPropertySource} that can track the {@link SystemEnvironmentOrigin} for
* every system environment property.
*
* @author Madhura Bhave
* @since 2.0.0
*/
public class SystemEnvironmentPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
/**
* The default order for the processor.
*/
public static final int DEFAULT_ORDER = SpringApplicationJsonEnvironmentPostProcessor.DEFAULT_ORDER - 1;
private int order = DEFAULT_ORDER;
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
PropertySource<?> propertySource = environment.getPropertySources().get(sourceName);
if (propertySource != null) {
replacePropertySource(environment, sourceName, propertySource);
}
}
@SuppressWarnings("unchecked")
private void replacePropertySource(ConfigurableEnvironment environment, String sourceName, PropertySource<?> propertySource) {
if (propertySource.getSource() instanceof Map) {
Map<String, Object> originalSource = (Map<String, Object>) propertySource.getSource();
Map<String, Object> originTrackedSource = new LinkedHashMap<>(originalSource);
originTrackedSource.entrySet().forEach(e -> e.setValue(OriginTrackedValue.of(e.getValue(),
new SystemEnvironmentOrigin(e.getKey()))));
OriginTrackedSystemPropertySource source = new OriginTrackedSystemPropertySource(sourceName,
Collections.unmodifiableMap(originTrackedSource));
environment.getPropertySources().replace(sourceName, source);
}
}
@Override
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.origin;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* {@link Origin} for an item loaded from the system environment. Provides access to the
* original property name.
*
* @author Madhura Bhave
* @since 2.0.0
*/
public class SystemEnvironmentOrigin implements Origin {
private final String property;
public SystemEnvironmentOrigin(String property) {
Assert.notNull(property, "Property name must not be null");
Assert.hasText(property, "Property name must not be empty");
this.property = property;
}
public String getProperty() {
return this.property;
}
@Override
public String toString() {
return "System Environment Property \"" + this.property + "\"";
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(this.property);
result = 31 * result + ObjectUtils.nullSafeHashCode(this.property);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
SystemEnvironmentOrigin other = (SystemEnvironmentOrigin) obj;
return ObjectUtils.nullSafeEquals(this.property, other.property);
}
}

View File

@ -33,7 +33,8 @@ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\

View File

@ -0,0 +1,78 @@
/*
* 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.env;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginTrackedValue;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link OriginTrackedSystemPropertySource}.
*
* @author Madhura Bhave
*/
public class OriginTrackedSystemPropertySourceTests {
private Map<String, Object> map = new LinkedHashMap<>();
private OriginTrackedSystemPropertySource source = new OriginTrackedSystemPropertySource(
"test", this.map);
private Origin origin = mock(Origin.class);
@Test
public void getPropertyWhenMissingShouldReturnNull() throws Exception {
assertThat(this.source.getProperty("test")).isNull();
}
@Test
public void getPropertyWhenNonTrackedShouldReturnValue() throws Exception {
this.map.put("test", "foo");
assertThat(this.source.getProperty("test")).isEqualTo("foo");
}
@Test
public void getPropertyWhenTrackedShouldReturnValue() throws Exception {
this.map.put("test", OriginTrackedValue.of("foo", this.origin));
assertThat(this.source.getProperty("test")).isEqualTo("foo");
}
@Test
public void getPropertyOriginWhenMissingShouldReturnNull() throws Exception {
assertThat(this.source.getOrigin("test")).isNull();
}
@Test
public void getPropertyOriginWhenNonTrackedShouldReturnNull() throws Exception {
this.map.put("test", "foo");
assertThat(this.source.getOrigin("test")).isNull();
}
@Test
public void getPropertyOriginWhenTrackedShouldReturnOrigin() throws Exception {
this.map.put("test", OriginTrackedValue.of("foo", this.origin));
assertThat(this.source.getOrigin("test")).isEqualTo(this.origin);
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.env;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.origin.OriginTrackedValue;
import org.springframework.boot.origin.SystemEnvironmentOrigin;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SystemEnvironmentPropertySourceEnvironmentPostProcessor}.
*
* @author Madhura Bhave
*/
public class SystemEnvironmentPropertySourceEnvironmentPostProcessorTests {
private ConfigurableEnvironment environment;
@Before
public void setUp() throws Exception {
this.environment = new StandardEnvironment();
}
@Test
public void postProcessShouldReplaceSystemEnviromentPropertySource() throws Exception {
SystemEnvironmentPropertySourceEnvironmentPostProcessor postProcessor = new SystemEnvironmentPropertySourceEnvironmentPostProcessor();
postProcessor.postProcessEnvironment(this.environment, null);
PropertySource<?> replaced = this.environment.getPropertySources().get("systemEnvironment");
assertThat(replaced).isInstanceOf(OriginTrackedSystemPropertySource.class);
}
@Test
@SuppressWarnings("unchecked")
public void replacedPropertySourceShouldHaveOriginTrackedValues() throws Exception {
SystemEnvironmentPropertySourceEnvironmentPostProcessor postProcessor = new SystemEnvironmentPropertySourceEnvironmentPostProcessor();
PropertySource<?> original = this.environment.getPropertySources().get("systemEnvironment");
postProcessor.postProcessEnvironment(this.environment, null);
PropertySource<?> replaced = this.environment.getPropertySources().get("systemEnvironment");
Map<String, Object> originalMap = (Map<String, Object>) original.getSource();
Map<String, OriginTrackedValue> replacedMap = (Map<String, OriginTrackedValue>) replaced.getSource();
for (Map.Entry<String, Object> entry : originalMap.entrySet()) {
OriginTrackedValue actual = replacedMap.get(entry.getKey());
assertThat(actual.getValue()).isEqualTo(entry.getValue());
assertThat(actual.getOrigin()).isEqualTo(new SystemEnvironmentOrigin(entry.getKey()));
}
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.origin;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SystemEnvironmentOrigin}.
*
* @author Madhura Bhave
*/
public class SystemEnvironmentOriginTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void createWhenPropertyIsNullShouldThrowException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
new SystemEnvironmentOrigin(null);
}
@Test
public void createWhenPropertyNameIsEmptyShouldThrowException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
new SystemEnvironmentOrigin("");
}
@Test
public void getPropertyShouldReturnProperty() throws Exception {
SystemEnvironmentOrigin origin = new SystemEnvironmentOrigin("FOO_BAR");
assertThat(origin.getProperty()).isEqualTo("FOO_BAR");
}
@Test
public void toStringShouldReturnStringWithDetails() throws Exception {
SystemEnvironmentOrigin origin = new SystemEnvironmentOrigin("FOO_BAR");
assertThat(origin.toString()).isEqualTo("System Environment Property \"FOO_BAR\"");
}
}