From d6a670e13e2ff10dc8d4bf2a92180fbf1c4f253d Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 26 Apr 2013 09:47:10 +0100 Subject: [PATCH] [bs-47] Add default command line args to SpringApplication * See SpringApplication.setDefaultCommandLineArgs * Assumes command line is in "simple" form (TODO: if we need JOpt etc. support then the command line args parsing from Spring needs to be exposed) * If defaults are provided the command line might be re-ordered slightly (non-option args come after option args) * Allows custom application.properties file names to be specified as a side effect (--spring.config.name=... or --spring.config.location=...}. [Fixes #48284369] --- .../bootstrap/SpringApplication.java | 82 ++++++++++++++++++- .../bootstrap/SpringApplicationTests.java | 21 +++++ 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/SpringApplication.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/SpringApplication.java index 7572bfff2b3..f6c7e965956 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/SpringApplication.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/SpringApplication.java @@ -19,7 +19,9 @@ package org.springframework.bootstrap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -33,7 +35,6 @@ import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; -import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.GenericTypeResolver; @@ -41,6 +42,7 @@ 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.PropertySource; import org.springframework.core.env.SimpleCommandLinePropertySource; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -151,6 +153,8 @@ public class SpringApplication { private List> initializers; + private String[] defaultCommandLineArgs; + /** * Crate a new {@link SpringApplication} instance. The application context will load * beans from the specified sources (see {@link SpringApplication class-level} @@ -316,13 +320,76 @@ public class SpringApplication { if (environment instanceof ConfigurableEnvironment) { ConfigurableEnvironment configurable = (ConfigurableEnvironment) environment; if (this.addCommandLineProperties) { - CommandLinePropertySource propertySource = new SimpleCommandLinePropertySource( - args); + PropertySource propertySource = new SimpleCommandLinePropertySource( + mergeCommandLineArgs(this.defaultCommandLineArgs, args)); configurable.getPropertySources().addFirst(propertySource); } } } + /** + * Merge two sets of command lines, the defaults and the ones passed in at run time. + * + * @param defaults the default values + * @param args the ones passed in at runtime + * @return a new command line + */ + protected String[] mergeCommandLineArgs(String[] defaults, String[] args) { + + if (defaults == null || defaults.length == 0) { + return args; + } + + List result = new ArrayList(); + Map options = new LinkedHashMap(); + + for (String arg : defaults) { + if (isOptionArg(arg)) { + addOptionArg(options, arg); + } else { + result.add(arg); + } + } + for (String arg : args) { + if (isOptionArg(arg)) { + addOptionArg(options, arg); + } else if (!result.contains(arg)) { + result.add(arg); + } + } + + List optionsList = new ArrayList(); + for (String key : options.keySet()) { + String value = options.get(key); + optionsList.add("--" + key + (value == null ? "" : "=" + value)); + } + result.addAll(0, optionsList); + + return result.toArray(new String[result.size()]); + + } + + private boolean isOptionArg(String arg) { + return arg.startsWith("--"); + } + + private void addOptionArg(Map map, String arg) { + String optionText = arg.substring(2, arg.length()); + String optionName; + String optionValue = null; + if (optionText.contains("=")) { + optionName = optionText.substring(0, optionText.indexOf("=")); + optionValue = optionText.substring(optionText.indexOf("=") + 1, + optionText.length()); + } else { + optionName = optionText; + } + if (optionName.isEmpty()) { + throw new IllegalArgumentException("Invalid argument syntax: " + arg); + } + map.put(optionName, optionValue); + } + /** * Load beans into the application context. * @param context the context to load beans into @@ -394,6 +461,15 @@ public class SpringApplication { this.addCommandLineProperties = addCommandLineProperties; } + /** + * Set some default command line arguments which can be overridden by those passed + * into the run methods. + * @param defaultCommandLineArgs the default command line args to set + */ + public void setDefaultCommandLineArgs(String... defaultCommandLineArgs) { + this.defaultCommandLineArgs = defaultCommandLineArgs; + } + /** * Sets the bean name generator that should be used when generating bean names. */ diff --git a/spring-bootstrap/src/test/java/org/springframework/bootstrap/SpringApplicationTests.java b/spring-bootstrap/src/test/java/org/springframework/bootstrap/SpringApplicationTests.java index 50fef77d5a3..81fdae2ce0f 100644 --- a/spring-bootstrap/src/test/java/org/springframework/bootstrap/SpringApplicationTests.java +++ b/spring-bootstrap/src/test/java/org/springframework/bootstrap/SpringApplicationTests.java @@ -24,6 +24,7 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultBeanNameGenerator; +import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration; import org.springframework.bootstrap.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; import org.springframework.bootstrap.context.embedded.jetty.JettyEmbeddedServletContainerFactory; import org.springframework.context.ApplicationContext; @@ -37,6 +38,7 @@ import org.springframework.context.support.StaticApplicationContext; import org.springframework.core.Ordered; import org.springframework.core.env.CommandLinePropertySource; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.DefaultResourceLoader; @@ -67,6 +69,13 @@ public class SpringApplicationTests { private ApplicationContext context; + private Environment getEnvironment() { + if (this.context instanceof ConfigurableApplicationContext) { + return ((ConfigurableApplicationContext) this.context).getEnvironment(); + } + throw new IllegalStateException("No Environment available"); + } + @After public void close() { if (this.context instanceof ConfigurableApplicationContext) { @@ -250,6 +259,17 @@ public class SpringApplicationTests { assertNotNull(this.context); } + @Test + public void defaultCommandLineArgs() throws Exception { + SpringApplication application = new SpringApplication(ExampleConfig.class); + application.setDefaultCommandLineArgs("--foo", "--bar=spam", "bucket"); + application.setWebEnvironment(false); + this.context = application.run("--bar=foo", "bucket", "crap"); + assertThat(this.context, instanceOf(AnnotationConfigApplicationContext.class)); + assertThat(getEnvironment().getProperty("bar"), equalTo("foo")); + assertThat(getEnvironment().getProperty("foo"), equalTo("")); + } + private boolean hasPropertySource(ConfigurableEnvironment environment, Class propertySourceClass) { for (PropertySource source : environment.getPropertySources()) { @@ -311,6 +331,7 @@ public class SpringApplicationTests { } @Configuration + @EnableAutoConfiguration static class ExampleWebConfig { @Bean