diff --git a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index 473d3d9bb52..7e1a8338514 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -19,8 +19,13 @@ package org.springframework.boot; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.Properties; import java.util.Set; import org.apache.commons.logging.Log; @@ -43,6 +48,8 @@ import org.springframework.core.GenericTypeResolver; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.env.CommandLinePropertySource; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; import org.springframework.core.env.SimpleCommandLinePropertySource; import org.springframework.core.env.StandardEnvironment; @@ -162,7 +169,9 @@ public class SpringApplication { private List> initializers; - private String[] defaultArgs; + private Map defaultProperties; + + private Set profiles = new HashSet(); /** * Crate a new {@link SpringApplication} instance. The application context will load @@ -255,6 +264,9 @@ public class SpringApplication { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); addPropertySources(environment, args); + for (String profile : this.profiles) { + environment.addActiveProfile(profile); + } // Call all remaining initializers callEnvironmentAwareSpringApplicationInitializers(args, environment); @@ -321,12 +333,11 @@ public class SpringApplication { * @param args run arguments */ protected void addPropertySources(ConfigurableEnvironment environment, String[] args) { + if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { + environment.getPropertySources().addLast( + new MapPropertySource("defaultProperties", this.defaultProperties)); + } if (this.addCommandLineProperties) { - if (this.defaultArgs != null) { - environment.getPropertySources().addFirst( - new SimpleCommandLinePropertySource("defaultCommandLineArgs", - this.defaultArgs)); - } environment.getPropertySources().addFirst( new SimpleCommandLinePropertySource(args)); } @@ -557,13 +568,34 @@ public class SpringApplication { } /** - * Set default arguments which will be used in addition to those specified to the - * {@code run} methods. Default arguments can always be overridden by user defined - * arguments.. - * @param defaultArgs the default args to set + * Set default environment properties which will be used in addition to those in the + * existing {@link Environment}. + * @param defaultProperties the additional properties to set + */ + public void setDefaultProperties(Map defaultProperties) { + this.defaultProperties = defaultProperties; + } + + /** + * Convenient alternative to {@link #setDefaultProperties(Map)}. + * + * @param defaultProperties some {@link Properties} + */ + public void setDefaultProperties(Properties defaultProperties) { + this.defaultProperties = new HashMap(); + for (Object key : Collections.list(defaultProperties.propertyNames())) { + this.defaultProperties.put((String) key, defaultProperties.get(key)); + } + } + + /** + * Set additional profile values to use (on top of those set in system or command line + * properties). + * + * @param profiles the additional profiles to set */ - public void setDefaultArgs(String... defaultArgs) { - this.defaultArgs = defaultArgs; + public void setAdditionalProfiles(Collection profiles) { + this.profiles = new LinkedHashSet(profiles); } /** diff --git a/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java b/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java index dd6d28c25e0..f06a44c6208 100644 --- a/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java +++ b/spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java @@ -16,7 +16,11 @@ package org.springframework.boot.builder; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -29,7 +33,6 @@ import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.io.ResourceLoader; -import org.springframework.util.StringUtils; /** * Builder for {@link SpringApplication} and {@link ApplicationContext} instances with @@ -62,8 +65,9 @@ public class SpringApplicationBuilder { private SpringApplicationBuilder parent; private AtomicBoolean running = new AtomicBoolean(false); private Set sources = new LinkedHashSet(); - private Set defaultArgs = new LinkedHashSet(); + private Map defaultProperties = new LinkedHashMap(); private ConfigurableEnvironment environment; + private Set additionalProfiles = new LinkedHashSet(); public SpringApplicationBuilder(Object... sources) { this.application = new SpringApplication(sources); @@ -134,8 +138,8 @@ public class SpringApplicationBuilder { child.sources(sources); // Copy environment stuff from parent to child - child.defaultArgs(this.defaultArgs.toArray(new String[this.defaultArgs.size()])) - .environment(this.environment); + child.properties(this.defaultProperties).environment(this.environment) + .additionalProfiles(this.additionalProfiles); child.parent = this; // It's not possible if embedded containers are enabled to support web contexts as @@ -163,7 +167,7 @@ public class SpringApplicationBuilder { public SpringApplicationBuilder parent(Object... sources) { if (this.parent == null) { this.parent = new SpringApplicationBuilder(sources).web(false) - .defaultArgs(this.defaultArgs).environment(this.environment); + .properties(this.defaultProperties).environment(this.environment); } else { this.parent.sources(sources); @@ -315,39 +319,66 @@ public class SpringApplicationBuilder { } /** - * Default command line arguments (overridden by explicit arguments at runtime in - * {@link #run(String...)}). + * Default properties for the environment in the form key=value or + * key:value. * - * @param defaultArgs the args to set. + * @param defaultProperties the properties to set. * @return the current builder */ - public SpringApplicationBuilder defaultArgs(String... defaultArgs) { - this.defaultArgs.addAll(Arrays.asList(defaultArgs)); - this.application.setDefaultArgs(this.defaultArgs - .toArray(new String[this.defaultArgs.size()])); + public SpringApplicationBuilder properties(String... defaultProperties) { + this.defaultProperties.putAll(getMapFromKeyValuePairs(defaultProperties)); + this.application.setDefaultProperties(this.defaultProperties); if (this.parent != null) { - this.parent.defaultArgs(defaultArgs); + this.parent.properties(defaultProperties); this.parent.environment(this.environment); } return this; } - private SpringApplicationBuilder defaultArgs(Set defaultArgs) { - this.defaultArgs = defaultArgs; + private Map getMapFromKeyValuePairs(String[] args) { + Map map = new HashMap(); + for (String pair : args) { + int index = pair.indexOf(":"); + if (index <= 0) { + index = pair.indexOf("="); + } + String key = pair.substring(0, index > 0 ? index : pair.length()); + String value = index > 0 ? pair.substring(index + 1) : ""; + map.put(key, value); + } + return map; + } + + /** + * Default properties for the environment. Multiple calls to this method are + * cumulative. + * + * @param defaults + * @return the current builder + * + * @see SpringApplicationBuilder#properties(String...) + */ + public SpringApplicationBuilder properties(Map defaults) { + this.defaultProperties.putAll(defaults); return this; } /** - * Set the active Spring profiles for this app (and its parent and children). - * Synonymous with {@link #defaultArgs(String...) - * defaultArgs("--spring.profiles.active=[profiles]")} + * Add to the active Spring profiles for this app (and its parent and children). * - * @param profiles the profiles to set. + * @param profiles the profiles to add. * @return the current builder */ public SpringApplicationBuilder profiles(String... profiles) { - defaultArgs("--spring.profiles.active=" - + StringUtils.arrayToCommaDelimitedString(profiles)); + this.additionalProfiles.addAll(Arrays.asList(profiles)); + this.application.setAdditionalProfiles(this.additionalProfiles); + return this; + } + + private SpringApplicationBuilder additionalProfiles( + Collection additionalProfiles) { + this.additionalProfiles = new LinkedHashSet(additionalProfiles); + this.application.setAdditionalProfiles(additionalProfiles); return this; } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/initializer/ConfigFileApplicationContextInitializer.java b/spring-boot/src/main/java/org/springframework/boot/context/initializer/ConfigFileApplicationContextInitializer.java index e5a759a7564..32944967ac5 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/initializer/ConfigFileApplicationContextInitializer.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/initializer/ConfigFileApplicationContextInitializer.java @@ -125,6 +125,8 @@ public class ConfigFileApplicationContextInitializer implements List candidates = getCandidateLocations(); Collections.reverse(candidates); + PropertySource removed = environment.getPropertySources().remove( + "defaultProperties"); List> sources = new ArrayList>(); // Initial load allows profiles to be activated @@ -150,6 +152,10 @@ public class ConfigFileApplicationContextInitializer implements for (PropertySource source : sources) { environment.getPropertySources().addLast(source); } + + if (removed != null) { + environment.getPropertySources().addLast(removed); + } } private List getCandidateLocations() { diff --git a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java index ebf099536dc..dce0a7ba165 100644 --- a/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java @@ -49,6 +49,7 @@ import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.util.StringUtils; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -301,7 +302,8 @@ public class SpringApplicationTests { @Test public void defaultCommandLineArgs() throws Exception { SpringApplication application = new SpringApplication(ExampleConfig.class); - application.setDefaultArgs("--baz", "--bar=spam", "bucket"); + application.setDefaultProperties(StringUtils.splitArrayElementsIntoProperties( + new String[] { "baz=", "bar=spam" }, "=")); application.setWebEnvironment(false); this.context = application.run("--bar=foo", "bucket", "crap"); assertThat(this.context, instanceOf(AnnotationConfigApplicationContext.class)); diff --git a/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java b/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java index d7a4cbe1c41..4fe3210c1bc 100644 --- a/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java @@ -18,7 +18,6 @@ package org.springframework.boot.builder; import org.junit.After; import org.junit.Test; -import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -48,14 +47,15 @@ public class SpringApplicationBuilderTests { } @Test - public void profileAndDefaultArgs() throws Exception { + public void profileAndProperties() throws Exception { SpringApplicationBuilder application = new SpringApplicationBuilder() .sources(ExampleConfig.class) .contextClass(StaticApplicationContext.class).profiles("foo") - .defaultArgs("--foo=bar"); + .properties("foo=bar"); this.context = application.run(); assertThat(this.context, is(instanceOf(StaticApplicationContext.class))); - assertThat(this.context.getEnvironment().getProperty("foo"), is(equalTo("bar"))); + assertThat(this.context.getEnvironment().getProperty("foo"), + is(equalTo("bucket"))); assertThat(this.context.getEnvironment().acceptsProfiles("foo"), is(true)); } @@ -90,7 +90,7 @@ public class SpringApplicationBuilderTests { @Test public void parentFirstCreationWithProfileAndDefaultArgs() throws Exception { SpringApplicationBuilder application = new SpringApplicationBuilder( - ExampleConfig.class).profiles("node").defaultArgs("--transport=redis") + ExampleConfig.class).profiles("node").properties("transport=redis") .child(ChildConfig.class).web(false); this.context = application.run(); assertThat(this.context.getEnvironment().acceptsProfiles("node"), is(true)); diff --git a/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java b/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java index 8377d4196a9..1712724fa03 100644 --- a/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java +++ b/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java @@ -18,8 +18,10 @@ package org.springframework.boot.test; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.springframework.beans.BeanUtils; @@ -34,7 +36,6 @@ import org.springframework.test.context.support.AbstractContextLoader; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.context.web.WebMergedContextConfiguration; import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; import org.springframework.web.context.support.GenericWebApplicationContext; /** @@ -63,16 +64,15 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { SpringApplication application = new SpringApplication(); application.setSources(sources); - Set args = new LinkedHashSet(); + Map args = new LinkedHashMap(); if (!ObjectUtils.isEmpty(mergedConfig.getActiveProfiles())) { - args.add("--spring.profiles.active=" - + StringUtils.arrayToCommaDelimitedString(mergedConfig - .getActiveProfiles())); + application.setAdditionalProfiles(Arrays.asList(mergedConfig + .getActiveProfiles())); } // Not running an embedded server, just setting up web context - args.add("--server.port=0"); - args.add("--management.port=0"); - application.setDefaultArgs(args.toArray(new String[args.size()])); + args.put("server.port", "0"); + args.put("management.port", "0"); + application.setDefaultProperties(args); List> initializers = new ArrayList>( application.getInitializers()); for (Class> type : mergedConfig