/* * Copyright 2013-2016 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.cloud.bootstrap.config; import java.io.File; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.bootstrap.TestHigherPriorityBootstrapConfiguration; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; 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 static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** * @author Dave Syer * */ public class BootstrapConfigurationTests { private ConfigurableApplicationContext context; private ConfigurableApplicationContext sibling; @Rule public ExpectedException expected = ExpectedException.none(); @After public void close() { // Expected.* is bound to the PropertySourceConfiguration below System.clearProperty("expected.name"); System.clearProperty("expected.fail"); // Used to test system properties override System.clearProperty("bootstrap.foo"); PropertySourceConfiguration.MAP.clear(); if (this.context != null) { this.context.close(); } if (this.sibling != null) { this.sibling.close(); } } @Test public void pickupExternalBootstrapProperties() { String externalPropertiesPath = getExternalProperties(); this.context = new SpringApplicationBuilder().web(false) .sources(BareConfiguration.class) .properties("spring.cloud.bootstrap.location:" + externalPropertiesPath) .run(); assertEquals("externalPropertiesInfoName", this.context.getEnvironment().getProperty("info.name")); assertTrue(this.context.getEnvironment().getPropertySources().contains( PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME)); } @Test public void bootstrapPropertiesAvailableInInitializer() { this.context = new SpringApplicationBuilder().web(false) .sources(BareConfiguration.class).initializers( new ApplicationContextInitializer<ConfigurableApplicationContext>() { @Override public void initialize( ConfigurableApplicationContext applicationContext) { // This property is defined in bootstrap.properties assertEquals("child", applicationContext.getEnvironment() .getProperty("info.name")); } }) .run(); assertTrue(this.context.getEnvironment().getPropertySources().contains( PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME)); } /** * Running the test from maven will start from a different directory then starting it * from intellij * * @return */ private String getExternalProperties() { String externalPropertiesPath = ""; File externalProperties = new File( "src/test/resources/external-properties/bootstrap.properties"); if (externalProperties.exists()) { externalPropertiesPath = externalProperties.getAbsolutePath(); } else { externalProperties = new File( "spring-cloud-context/src/test/resources/external-properties/bootstrap.properties"); externalPropertiesPath = externalProperties.getAbsolutePath(); } return externalPropertiesPath; } @Test public void picksUpAdditionalPropertySource() { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); this.context = new SpringApplicationBuilder().web(false) .sources(BareConfiguration.class).run(); assertEquals("bar", this.context.getEnvironment().getProperty("bootstrap.foo")); assertTrue(this.context.getEnvironment().getPropertySources().contains( PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME)); } @Test public void failsOnPropertySource() { System.setProperty("expected.fail", "true"); this.expected.expectMessage("Planned"); this.context = new SpringApplicationBuilder().web(false) .sources(BareConfiguration.class).run(); } @Test public void overrideSystemPropertySourceByDefault() { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); System.setProperty("bootstrap.foo", "system"); this.context = new SpringApplicationBuilder().web(false) .sources(BareConfiguration.class).run(); assertEquals("bar", this.context.getEnvironment().getProperty("bootstrap.foo")); } @Test public void systemPropertyOverrideFalse() { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); PropertySourceConfiguration.MAP .put("spring.cloud.config.overrideSystemProperties", "false"); System.setProperty("bootstrap.foo", "system"); this.context = new SpringApplicationBuilder().web(false) .sources(BareConfiguration.class).run(); assertEquals("system", this.context.getEnvironment().getProperty("bootstrap.foo")); } @Test public void systemPropertyOverrideWhenOverrideDisallowed() { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); PropertySourceConfiguration.MAP .put("spring.cloud.config.overrideSystemProperties", "false"); // If spring.cloud.config.allowOverride=false is in the remote property sources // with sufficiently high priority it always wins. Admins can enforce it by adding // their own remote property source. PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "false"); System.setProperty("bootstrap.foo", "system"); this.context = new SpringApplicationBuilder().web(false) .sources(BareConfiguration.class).run(); assertEquals("bar", this.context.getEnvironment().getProperty("bootstrap.foo")); } @Test public void systemPropertyOverrideFalseWhenOverrideAllowed() { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); PropertySourceConfiguration.MAP .put("spring.cloud.config.overrideSystemProperties", "false"); PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "true"); System.setProperty("bootstrap.foo", "system"); this.context = new SpringApplicationBuilder().web(false) .sources(BareConfiguration.class).run(); assertEquals("system", this.context.getEnvironment().getProperty("bootstrap.foo")); } @Test public void overrideAllWhenOverrideAllowed() { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); PropertySourceConfiguration.MAP.put("spring.cloud.config.overrideNone", "true"); PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "true"); ConfigurableEnvironment environment = new StandardEnvironment(); environment.getPropertySources().addLast(new MapPropertySource("last", Collections.<String, Object>singletonMap("bootstrap.foo", "splat"))); this.context = new SpringApplicationBuilder().web(false).environment(environment) .sources(BareConfiguration.class).run(); assertEquals("splat", this.context.getEnvironment().getProperty("bootstrap.foo")); } @Test public void applicationNameInBootstrapAndMain() { System.setProperty("expected.name", "main"); this.context = new SpringApplicationBuilder().web(false) .properties("spring.cloud.bootstrap.name:other", "spring.config.name:plain") .sources(BareConfiguration.class).run(); assertEquals("app", this.context.getEnvironment().getProperty("spring.application.name")); // The parent is called "main" because spring.application.name is specified in // other.properties (the bootstrap properties) assertEquals("main", this.context.getParent().getEnvironment() .getProperty("spring.application.name")); // The bootstrap context has the same "bootstrap" property source assertEquals(this.context.getEnvironment().getPropertySources().get("bootstrap"), ((ConfigurableEnvironment) this.context.getParent().getEnvironment()) .getPropertySources().get("bootstrap")); assertEquals("app", this.context.getId()); } @Test public void applicationNameNotInBootstrap() { System.setProperty("expected.name", "main"); this.context = new SpringApplicationBuilder().web(false) .properties("spring.cloud.bootstrap.name:application", "spring.config.name:other") .sources(BareConfiguration.class).run(); assertEquals("main", this.context.getEnvironment().getProperty("spring.application.name")); // The parent has no name because spring.application.name is not // defined in the bootstrap properties assertEquals(null, this.context.getParent().getEnvironment() .getProperty("spring.application.name")); } @Test public void applicationNameOnlyInBootstrap() { System.setProperty("expected.name", "main"); this.context = new SpringApplicationBuilder().web(false) .properties("spring.cloud.bootstrap.name:other") .sources(BareConfiguration.class).run(); // The main context is called "main" because spring.application.name is specified // in other.properties (and not in the main config file) assertEquals("main", this.context.getEnvironment().getProperty("spring.application.name")); // The parent is called "main" because spring.application.name is specified in // other.properties (the bootstrap properties this time) assertEquals("main", this.context.getParent().getEnvironment() .getProperty("spring.application.name")); assertEquals("main", this.context.getId()); } @Test public void environmentEnrichedOnceWhenSharedWithChildContext() { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) .environment(new StandardEnvironment()).child(BareConfiguration.class) .web(false).run(); assertEquals("bar", this.context.getEnvironment().getProperty("bootstrap.foo")); assertEquals(this.context.getEnvironment(), this.context.getParent().getEnvironment()); MutablePropertySources sources = this.context.getEnvironment() .getPropertySources(); PropertySource<?> bootstrap = sources .get(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME); assertNotNull(bootstrap); assertEquals(0, sources.precedenceOf(bootstrap)); } @Test public void onlyOneBootstrapContext() { TestHigherPriorityBootstrapConfiguration.count.set(0); PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) .child(BareConfiguration.class).web(false).run(); assertEquals(1, TestHigherPriorityBootstrapConfiguration.count.get()); assertNotNull(context.getParent()); assertEquals("bootstrap", context.getParent().getParent().getId()); assertNull(context.getParent().getParent().getParent()); assertEquals("bar", context.getEnvironment().getProperty("custom.foo")); } @Test public void bootstrapContextSharedBySiblings() { TestHigherPriorityBootstrapConfiguration.count.set(0); PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); SpringApplicationBuilder builder = new SpringApplicationBuilder() .sources(BareConfiguration.class); this.sibling = builder.child(BareConfiguration.class) .properties("spring.application.name=sibling").web(false).run(); this.context = builder.child(BareConfiguration.class) .properties("spring.application.name=context").web(false).run(); assertEquals(1, TestHigherPriorityBootstrapConfiguration.count.get()); assertNotNull(context.getParent()); assertEquals("bootstrap", context.getParent().getParent().getId()); assertNull(context.getParent().getParent().getParent()); assertEquals("context", context.getEnvironment().getProperty("custom.foo")); assertEquals("context", context.getEnvironment().getProperty("spring.application.name")); assertNotNull(sibling.getParent()); assertEquals("bootstrap", sibling.getParent().getParent().getId()); assertNull(sibling.getParent().getParent().getParent()); assertEquals("sibling", sibling.getEnvironment().getProperty("custom.foo")); assertEquals("sibling", sibling.getEnvironment().getProperty("spring.application.name")); } @Test public void environmentEnrichedInParentContext() { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) .child(BareConfiguration.class).web(false).run(); assertEquals("bar", this.context.getEnvironment().getProperty("bootstrap.foo")); assertNotSame(this.context.getEnvironment(), this.context.getParent().getEnvironment()); assertTrue(this.context.getEnvironment().getPropertySources().contains( PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME)); assertTrue(((ConfigurableEnvironment) this.context.getParent().getEnvironment()) .getPropertySources().contains( PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME)); } @Test public void differentProfileInChild() { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); // Profiles are always merged with the child ConfigurableApplicationContext parent = new SpringApplicationBuilder() .sources(BareConfiguration.class).profiles("parent").web(false).run(); this.context = new SpringApplicationBuilder(BareConfiguration.class) .profiles("child").parent(parent).web(false).run(); assertNotSame(this.context.getEnvironment(), this.context.getParent().getEnvironment()); // The ApplicationContext merges profiles (profiles and property sources), see // AbstractEnvironment.merge() assertTrue(this.context.getEnvironment().acceptsProfiles("child", "parent")); // But the parent is not a child assertFalse(this.context.getParent().getEnvironment().acceptsProfiles("child")); assertTrue(this.context.getParent().getEnvironment().acceptsProfiles("parent")); assertTrue(((ConfigurableEnvironment) this.context.getParent().getEnvironment()) .getPropertySources().contains( PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME)); assertEquals("bar", this.context.getEnvironment().getProperty("bootstrap.foo")); // The "bootstrap" property source is not shared now, but it has the same // properties in it because they are pulled from the PropertySourceConfiguration // below assertEquals("bar", this.context.getParent().getEnvironment().getProperty("bootstrap.foo")); // The parent property source is there in the child because they are both in the // "parent" profile (by virtue of the merge in AbstractEnvironment) assertEquals("parent", this.context.getEnvironment().getProperty("info.name")); } @Test public void includeProfileFromBootstrapPropertySource() { PropertySourceConfiguration.MAP.put("spring.profiles.include", "bar,baz"); this.context = new SpringApplicationBuilder().web(false).profiles("foo") .sources(BareConfiguration.class).run(); assertTrue(this.context.getEnvironment().acceptsProfiles("baz")); assertTrue(this.context.getEnvironment().acceptsProfiles("bar")); } @Configuration @EnableConfigurationProperties protected static class BareConfiguration { } @Configuration @ConfigurationProperties("expected") // This is added to bootstrap context as a source in bootstrap.properties protected static class PropertySourceConfiguration implements PropertySourceLocator { public static Map<String, Object> MAP = new HashMap<String, Object>( Collections.<String, Object>singletonMap("bootstrap.foo", "bar")); private String name; private boolean fail = false; @Override public PropertySource<?> locate(Environment environment) { if (this.name != null) { assertEquals(this.name, environment.getProperty("spring.application.name")); } if (this.fail) { throw new RuntimeException("Planned"); } return new MapPropertySource("testBootstrap", MAP); } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public boolean isFail() { return this.fail; } public void setFail(boolean fail) { this.fail = fail; } } }