diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/endpoint/RefreshEndpoint.java b/spring-cloud-context/src/main/java/org/springframework/cloud/endpoint/RefreshEndpoint.java index ae5c257f..41096da6 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/endpoint/RefreshEndpoint.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/endpoint/RefreshEndpoint.java @@ -16,13 +16,16 @@ package org.springframework.cloud.endpoint; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import org.springframework.boot.Banner.Mode; import org.springframework.boot.actuate.endpoint.AbstractEndpoint; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -49,12 +52,12 @@ import org.springframework.web.context.support.StandardServletEnvironment; @ManagedResource public class RefreshEndpoint extends AbstractEndpoint> { - private Set standardSources = new HashSet(Arrays.asList( - StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, - StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, - StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME, - StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, - StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); + private Set standardSources = new HashSet( + Arrays.asList(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, + StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME, + StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, + StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); private ConfigurableApplicationContext context; @@ -68,8 +71,8 @@ public class RefreshEndpoint extends AbstractEndpoint> { @ManagedOperation public synchronized String[] refresh() { - Map before = extract(this.context.getEnvironment() - .getPropertySources()); + Map before = extract( + this.context.getEnvironment().getPropertySources()); addConfigFilesToEnvironment(); Set keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet(); @@ -81,23 +84,33 @@ public class RefreshEndpoint extends AbstractEndpoint> { private void addConfigFilesToEnvironment() { ConfigurableApplicationContext capture = null; try { - StandardEnvironment environment = copyEnvironment(this.context.getEnvironment()); - capture = new SpringApplicationBuilder(Empty.class).showBanner(false) + StandardEnvironment environment = copyEnvironment( + this.context.getEnvironment()); + capture = new SpringApplicationBuilder(Empty.class).bannerMode(Mode.OFF) .web(false).environment(environment).run(); MutablePropertySources target = this.context.getEnvironment() .getPropertySources(); + String targetName = null; for (PropertySource source : environment.getPropertySources()) { String name = source.getName(); + if (target.contains(name)) { + targetName = name; + } if (!this.standardSources.contains(name)) { if (target.contains(name)) { target.replace(name, source); } else { - if (target.contains("defaultProperties")) { - target.addBefore("defaultProperties", source); + if (targetName != null) { + target.addAfter(targetName, source); } else { - target.addLast(source); + if (target.contains("defaultProperties")) { + target.addBefore("defaultProperties", source); + } + else { + target.addLast(source); + } } } } @@ -118,16 +131,15 @@ public class RefreshEndpoint extends AbstractEndpoint> { } } - // Don't use ConfigurableEnvironment.merge() in case there are clashes with property source names + // Don't use ConfigurableEnvironment.merge() in case there are clashes with property + // source names private StandardEnvironment copyEnvironment(ConfigurableEnvironment input) { StandardEnvironment environment = new StandardEnvironment(); - MutablePropertySources capturedPropertySources = environment - .getPropertySources(); + MutablePropertySources capturedPropertySources = environment.getPropertySources(); for (PropertySource source : capturedPropertySources) { capturedPropertySources.remove(source.getName()); } - for (PropertySource source : input - .getPropertySources()) { + for (PropertySource source : input.getPropertySources()) { capturedPropertySources.addLast(source); } environment.setActiveProfiles(input.getActiveProfiles()); @@ -171,9 +183,13 @@ public class RefreshEndpoint extends AbstractEndpoint> { private Map extract(MutablePropertySources propertySources) { Map result = new HashMap(); - for (PropertySource parent : propertySources) { - if (!this.standardSources.contains(parent.getName())) { - extract(parent, result); + List> sources = new ArrayList>(); + for (PropertySource source : propertySources) { + sources.add(0, source); + } + for (PropertySource source : sources) { + if (!this.standardSources.contains(source.getName())) { + extract(source, result); } } return result; @@ -182,8 +198,12 @@ public class RefreshEndpoint extends AbstractEndpoint> { private void extract(PropertySource parent, Map result) { if (parent instanceof CompositePropertySource) { try { + List> sources = new ArrayList>(); for (PropertySource source : ((CompositePropertySource) parent) .getPropertySources()) { + sources.add(0, source); + } + for (PropertySource source : sources) { extract(source, result); } } diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/endpoint/RefreshEndpointTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/endpoint/RefreshEndpointTests.java index d794abfd..af345e0f 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/endpoint/RefreshEndpointTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/endpoint/RefreshEndpointTests.java @@ -16,17 +16,17 @@ package org.springframework.cloud.endpoint; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import org.junit.After; import org.junit.Test; +import org.springframework.boot.Banner.Mode; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.cloud.context.scope.refresh.RefreshScope; import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent; @@ -37,6 +37,9 @@ import org.springframework.context.event.EventListener; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + /** * @author Dave Syer * @@ -47,15 +50,42 @@ public class RefreshEndpointTests { @After public void close() { - if (this.context!=null) { + if (this.context != null) { this.context.close(); } } + @Test + public void keysComputedWhenAdded() throws Exception { + this.context = new SpringApplicationBuilder(Empty.class).web(false) + .bannerMode(Mode.OFF).properties("spring.cloud.bootstrap.name:none") + .run(); + RefreshScope scope = new RefreshScope(); + scope.setApplicationContext(this.context); + EnvironmentTestUtils.addEnvironment(this.context, "spring.profiles.active=local"); + RefreshEndpoint endpoint = new RefreshEndpoint(this.context, scope); + Collection keys = endpoint.invoke(); + assertTrue("Wrong keys: " + keys, keys.contains("added")); + } + + @Test + public void keysComputedWhenOveridden() throws Exception { + this.context = new SpringApplicationBuilder(Empty.class).web(false) + .bannerMode(Mode.OFF).properties("spring.cloud.bootstrap.name:none") + .run(); + RefreshScope scope = new RefreshScope(); + scope.setApplicationContext(this.context); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.profiles.active=override"); + RefreshEndpoint endpoint = new RefreshEndpoint(this.context, scope); + Collection keys = endpoint.invoke(); + assertTrue("Wrong keys: " + keys, keys.contains("message")); + } + @Test public void eventsPublishedInOrder() throws Exception { - this.context = new SpringApplicationBuilder(Empty.class) - .web(false).showBanner(false).run(); + this.context = new SpringApplicationBuilder(Empty.class).web(false) + .bannerMode(Mode.OFF).run(); RefreshScope scope = new RefreshScope(); scope.setApplicationContext(this.context); RefreshEndpoint endpoint = new RefreshEndpoint(this.context, scope); @@ -69,7 +99,7 @@ public class RefreshEndpointTests { @Test public void shutdownHooksCleaned() { ConfigurableApplicationContext context = new SpringApplicationBuilder(Empty.class) - .web(false).showBanner(false).run(); + .web(false).bannerMode(Mode.OFF).run(); RefreshScope scope = new RefreshScope(); scope.setApplicationContext(context); RefreshEndpoint endpoint = new RefreshEndpoint(context, scope); @@ -92,10 +122,12 @@ public class RefreshEndpointTests { @Configuration protected static class Empty { private List events = new ArrayList(); + @EventListener(EnvironmentChangeEvent.class) public void changed(EnvironmentChangeEvent event) { this.events.add(event); } + @EventListener(RefreshScopeRefreshedEvent.class) public void refreshed(RefreshScopeRefreshedEvent event) { this.events.add(event); diff --git a/spring-cloud-context/src/test/resources/application-local.properties b/spring-cloud-context/src/test/resources/application-local.properties new file mode 100644 index 00000000..4da25c00 --- /dev/null +++ b/spring-cloud-context/src/test/resources/application-local.properties @@ -0,0 +1 @@ +added: Hello added! diff --git a/spring-cloud-context/src/test/resources/application-override.properties b/spring-cloud-context/src/test/resources/application-override.properties new file mode 100644 index 00000000..ea5f7924 --- /dev/null +++ b/spring-cloud-context/src/test/resources/application-override.properties @@ -0,0 +1 @@ +message: Hello override!