Browse Source

Use additional properties instead of default args

SpringApplication (and the builder) now do not accept default
command line args because the override semantics were wrong -
you need to be able to override them with everything, including
system properties and external properties (per profile).

Also implements new semantics for profiles - you can add
additional profiles in the Java API and they will not be
squashed by command line or system properties entries for
spring.profiles.active.

[Fixes #58989906] [bs-336] DefaultArgs -> DefaultProperties
pull/97/merge
Dave Syer 12 years ago
parent
commit
08cf5b4139
  1. 56
      spring-boot/src/main/java/org/springframework/boot/SpringApplication.java
  2. 73
      spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java
  3. 6
      spring-boot/src/main/java/org/springframework/boot/context/initializer/ConfigFileApplicationContextInitializer.java
  4. 4
      spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java
  5. 10
      spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java
  6. 16
      spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java

56
spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

@ -19,8 +19,13 @@ package org.springframework.boot; @@ -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; @@ -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 { @@ -162,7 +169,9 @@ public class SpringApplication {
private List<ApplicationContextInitializer<?>> initializers;
private String[] defaultArgs;
private Map<String, Object> defaultProperties;
private Set<String> profiles = new HashSet<String>();
/**
* Crate a new {@link SpringApplication} instance. The application context will load
@ -255,6 +264,9 @@ public class SpringApplication { @@ -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 { @@ -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 { @@ -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<String, Object> defaultProperties) {
this.defaultProperties = defaultProperties;
}
/**
* Convenient alternative to {@link #setDefaultProperties(Map)}.
*
* @param defaultProperties some {@link Properties}
*/
public void setDefaultProperties(Properties defaultProperties) {
this.defaultProperties = new HashMap<String, Object>();
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<String> profiles) {
this.profiles = new LinkedHashSet<String>(profiles);
}
/**

73
spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java

@ -16,7 +16,11 @@ @@ -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; @@ -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 { @@ -62,8 +65,9 @@ public class SpringApplicationBuilder {
private SpringApplicationBuilder parent;
private AtomicBoolean running = new AtomicBoolean(false);
private Set<Object> sources = new LinkedHashSet<Object>();
private Set<String> defaultArgs = new LinkedHashSet<String>();
private Map<String, Object> defaultProperties = new LinkedHashMap<String, Object>();
private ConfigurableEnvironment environment;
private Set<String> additionalProfiles = new LinkedHashSet<String>();
public SpringApplicationBuilder(Object... sources) {
this.application = new SpringApplication(sources);
@ -134,8 +138,8 @@ public class SpringApplicationBuilder { @@ -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 { @@ -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 { @@ -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 <code>key=value</code> or
* <code>key:value</code>.
*
* @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<String> defaultArgs) {
this.defaultArgs = defaultArgs;
private Map<String, Object> getMapFromKeyValuePairs(String[] args) {
Map<String, Object> map = new HashMap<String, Object>();
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<String, Object> 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<String> additionalProfiles) {
this.additionalProfiles = new LinkedHashSet<String>(additionalProfiles);
this.application.setAdditionalProfiles(additionalProfiles);
return this;
}

6
spring-boot/src/main/java/org/springframework/boot/context/initializer/ConfigFileApplicationContextInitializer.java

@ -125,6 +125,8 @@ public class ConfigFileApplicationContextInitializer implements @@ -125,6 +125,8 @@ public class ConfigFileApplicationContextInitializer implements
List<String> candidates = getCandidateLocations();
Collections.reverse(candidates);
PropertySource<?> removed = environment.getPropertySources().remove(
"defaultProperties");
List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
// Initial load allows profiles to be activated
@ -150,6 +152,10 @@ public class ConfigFileApplicationContextInitializer implements @@ -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<String> getCandidateLocations() {

4
spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java

@ -49,6 +49,7 @@ import org.springframework.core.env.StandardEnvironment; @@ -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 { @@ -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));

10
spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java

@ -18,7 +18,6 @@ package org.springframework.boot.builder; @@ -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 { @@ -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 { @@ -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));

16
spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoader.java

@ -18,8 +18,10 @@ package org.springframework.boot.test; @@ -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; @@ -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 { @@ -63,16 +64,15 @@ public class SpringApplicationContextLoader extends AbstractContextLoader {
SpringApplication application = new SpringApplication();
application.setSources(sources);
Set<String> args = new LinkedHashSet<String>();
Map<String, Object> args = new LinkedHashMap<String, Object>();
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<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>(
application.getInitializers());
for (Class<? extends ApplicationContextInitializer<?>> type : mergedConfig

Loading…
Cancel
Save