diff --git a/README.md b/README.md index 79905403..bd63cdd0 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,24 @@ the bootstrap phase of an application context), e.g. spring.platform.config.uri: http://myconfigserver.com ``` +The bootstrap properties will show up in the `/env` endpoint as a +high-priority property source, e.g. + +``` +$ curl localhost:8080/env +{ + "profiles":[], + "configService:https://github.com/scratches/config-repo/bar.properties":{"foo":"bar"}, + "servletContextInitParams":{}, + "systemProperties":{...}, + ... +} +``` + +(a property source called "configService:/" contains the property "foo" with value +"bar" and is highest priority). + ## Sample Application There is a sample application @@ -78,3 +96,23 @@ environment property from the git configuration repo is present. To change the location of the config server just set "spring.platform.config.uri" in "bootstrap.yml" (or via System properties etc.). + +The test case has a `main()` method that runs the server in the same +way (watch the logs for its port), so you can run the whole system in +one process and play with it (e.g. right click on the main in your IDE +and run it). The `main()` method uses `target/config` for the working +directory of the git repository, so you can make local changes there +and see them reflected in the running app. + +``` +$ curl localhost:8080/env/foo +bar +$ vi target/config/bar.properties +.. change value of "foo", optionally commit +$ curl localhost:8080/refresh +["foo"] +$ curl localhost:8080/env/foo +baz +``` + +The refresh endpoint reports that the "foo" property changed. diff --git a/spring-platform-config-client/README.md b/spring-platform-config-client/README.md index d6329124..1fa7ca11 100644 --- a/spring-platform-config-client/README.md +++ b/spring-platform-config-client/README.md @@ -22,7 +22,15 @@ adapt to its environment in a completely flexible way. **Autoconfiguration** - An application using `@EnableAutoConfiguration` will pick up beans for the components -below, notably `EnvironmentManager` and `RefreshScope`. +below, notably `RefreshEndpoint`, `EnvironmentManager` and +`RefreshScope`. + +## RefreshEndpoint + +Re-initializes the bootstrap environment, and sends a +`EnvironmentChangeEvent` if any properties changed. Both the +`RefreshScope` and the `ConfigurationPropertiesRebinder` respond to +this event. Exposed at `/refresh`. ## EnvironmentManager and EnvironmentManagerEndpoint diff --git a/spring-platform-config-client/src/main/java/org/springframework/platform/bootstrap/BootstrapApplicationListener.java b/spring-platform-config-client/src/main/java/org/springframework/platform/bootstrap/BootstrapApplicationListener.java index 21737293..f571a819 100644 --- a/spring-platform-config-client/src/main/java/org/springframework/platform/bootstrap/BootstrapApplicationListener.java +++ b/spring-platform-config-client/src/main/java/org/springframework/platform/bootstrap/BootstrapApplicationListener.java @@ -98,7 +98,6 @@ public class BootstrapApplicationListener implements builder.sources(names.toArray()); final ConfigurableApplicationContext context = builder.run(); // Shutdown the bootstrap context when the app closes - // TODO: maybe make the bootstrap context a parent of the app context application.addListeners(new ApplicationListener() { @Override @@ -106,6 +105,7 @@ public class BootstrapApplicationListener implements context.close(); } }); + // Make the bootstrap context a parent of the app context application.addInitializers(new AncestorInitializer(context)); return context; } @@ -146,11 +146,19 @@ public class BootstrapApplicationListener implements @Override public void initialize(ConfigurableApplicationContext context) { + preemptMerge(context.getEnvironment().getPropertySources(), parent.getEnvironment().getPropertySources().get("bootstrap")); while (context.getParent()!=null) { context = (ConfigurableApplicationContext) context.getParent(); } context.setParent(parent); } + + private void preemptMerge(MutablePropertySources propertySources, + PropertySource propertySource) { + if (propertySource!=null && !propertySources.contains(propertySource.getName())) { + propertySources.addFirst(propertySource); + } + } } diff --git a/spring-platform-config-client/src/test/java/org/springframework/platform/bootstrap/BootstrapConfigurationTests.java b/spring-platform-config-client/src/test/java/org/springframework/platform/bootstrap/BootstrapConfigurationTests.java index 5fd16cef..ed51b45b 100644 --- a/spring-platform-config-client/src/test/java/org/springframework/platform/bootstrap/BootstrapConfigurationTests.java +++ b/spring-platform-config-client/src/test/java/org/springframework/platform/bootstrap/BootstrapConfigurationTests.java @@ -17,6 +17,7 @@ package org.springframework.platform.bootstrap; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; @@ -29,6 +30,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.core.env.StandardEnvironment; import org.springframework.platform.config.client.PropertySourceLocator; @@ -62,7 +64,10 @@ public class BootstrapConfigurationTests { new StandardEnvironment()).child(BareConfiguration.class).web(false).run(); assertEquals("bar", context.getEnvironment().getProperty("bootstrap.foo")); assertEquals(context.getEnvironment(), context.getParent().getEnvironment()); - assertTrue(context.getEnvironment().getPropertySources().contains("bootstrap")); + MutablePropertySources sources = context.getEnvironment().getPropertySources(); + PropertySource bootstrap = sources.get("bootstrap"); + assertNotNull(bootstrap); + assertEquals(0, sources.precedenceOf(bootstrap)); } @Test