[bs-175] Generic dispatcher features for SpringApplication
* Move Spring.main into SpringApplication.main * User can bind command line or application.properties into SpringApplication * User can provide sources dynamically with --spring.main.sources (a CSV list of class names, package names or XML resource locations) * One side effect was to make DocumentMatchers stateless [#52830829]
This commit is contained in:
parent
d75c1e4956
commit
4ce6b64dce
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2013 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.autoconfigure.main;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.bootstrap.SpringApplication;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.SpringVersion;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Very simple main class that can be used to launch an application from sources (class,
|
||||
* package or XML). Useful for demos and testing, perhaps less for production use (where
|
||||
* the {@link SpringApplication} run methods are often more convenient).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan
|
||||
public abstract class Spring {
|
||||
|
||||
// FIXME can we delete this? is it used? does it belong here
|
||||
|
||||
private static ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(
|
||||
true);
|
||||
|
||||
private static ApplicationContext context;
|
||||
|
||||
/**
|
||||
* @return the context if there is one
|
||||
*/
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic main that can be used to launch an application.
|
||||
*
|
||||
* @param args command line arguments
|
||||
* @see SpringApplication#run(Object[], String[])
|
||||
* @see SpringApplication#run(Object, String...)
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
List<String> strings = new ArrayList<String>();
|
||||
List<Object> sources = new ArrayList<Object>();
|
||||
|
||||
for (String arg : args) {
|
||||
if (ClassUtils.isPresent(arg, null)) {
|
||||
sources.add(ClassUtils.forName(arg, null));
|
||||
}
|
||||
else if (arg.endsWith(".xml")) {
|
||||
sources.add(arg);
|
||||
}
|
||||
else if (!scanner.findCandidateComponents(arg).isEmpty()) {
|
||||
sources.add(arg);
|
||||
}
|
||||
else {
|
||||
strings.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (sources.isEmpty()) {
|
||||
sources.add(Spring.class);
|
||||
}
|
||||
|
||||
context = SpringApplication.run(sources.toArray(new Object[sources.size()]),
|
||||
strings.toArray(new String[strings.size()]));
|
||||
|
||||
LogFactory.getLog(Spring.class).info(
|
||||
"Running Spring " + SpringVersion.getVersion() + " with sources: "
|
||||
+ sources);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -20,7 +20,7 @@ import org.junit.Ignore;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
import org.springframework.autoconfigure.main.SimpleMainTests;
|
||||
import org.springframework.bootstrap.SimpleMainTests;
|
||||
import org.springframework.bootstrap.context.embedded.jetty.JettyEmbeddedServletContainerFactoryTests;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2013 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.autoconfigure.main;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.autoconfigure.main.Spring;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* Tests for {@link Spring}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class SimpleMainTests {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (this.context instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) this.context).close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyApplicationContext() throws Exception {
|
||||
Spring.main(new String[0]);
|
||||
this.context = Spring.getApplicationContext();
|
||||
assertNotNull(this.context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basePackageScan() throws Exception {
|
||||
Spring.main(new String[] { ClassUtils.getPackageName(Spring.class) });
|
||||
this.context = Spring.getApplicationContext();
|
||||
assertNotNull(this.context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlContext() throws Exception {
|
||||
Spring.main(new String[] { Spring.class.getName(),
|
||||
"org/springframework/bootstrap/sample-beans.xml" });
|
||||
this.context = Spring.getApplicationContext();
|
||||
assertNotNull(this.context);
|
||||
}
|
||||
|
||||
}
|
|
@ -29,10 +29,14 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
|||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Loads bean definitions from underlying sources, including XML and JavaConfig. Acts as a
|
||||
|
@ -162,13 +166,37 @@ class BeanDefinitionLoader {
|
|||
if (loadedResource != null && loadedResource.exists()) {
|
||||
return load(loadedResource);
|
||||
}
|
||||
Package packageResource = Package.getPackage(source.toString());
|
||||
Package packageResource = findPackage(source);
|
||||
if (packageResource != null) {
|
||||
return load(packageResource);
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid source '" + source + "'");
|
||||
}
|
||||
|
||||
private Package findPackage(CharSequence source) {
|
||||
Package pkg = Package.getPackage(source.toString());
|
||||
if (pkg != null) {
|
||||
return pkg;
|
||||
}
|
||||
try {
|
||||
// Attempt to find a class in this package
|
||||
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
|
||||
getClass().getClassLoader());
|
||||
Resource[] resources = resolver.getResources(ClassUtils
|
||||
.convertClassNameToResourcePath(source.toString()) + "/*.class");
|
||||
for (Resource resource : resources) {
|
||||
String className = StringUtils.stripFilenameExtension(resource
|
||||
.getFilename());
|
||||
load(Class.forName(source.toString() + "." + className));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// swallow exception and continue
|
||||
}
|
||||
return Package.getPackage(source.toString());
|
||||
}
|
||||
|
||||
private boolean isComponent(Class<?> type) {
|
||||
// This has to be a bit of a guess. The only way to be sure that this type is
|
||||
// eligible is to make a bean definition out of it and try to instantiate it.
|
||||
|
|
|
@ -19,9 +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.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
@ -36,20 +36,19 @@ 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.Configuration;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
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.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
|
||||
/**
|
||||
|
@ -133,7 +132,7 @@ public class SpringApplication {
|
|||
|
||||
private final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
private Object[] sources;
|
||||
private Set<Object> sources = new LinkedHashSet<Object>();
|
||||
|
||||
private Class<?> mainApplicationClass;
|
||||
|
||||
|
@ -169,8 +168,7 @@ public class SpringApplication {
|
|||
* @see #SpringApplication(ResourceLoader, Object...)
|
||||
*/
|
||||
public SpringApplication(Object... sources) {
|
||||
Assert.notEmpty(sources, "Sources must not be empty");
|
||||
this.sources = sources;
|
||||
addSources(sources);
|
||||
initialize();
|
||||
}
|
||||
|
||||
|
@ -185,12 +183,20 @@ public class SpringApplication {
|
|||
* @see #SpringApplication(ResourceLoader, Object...)
|
||||
*/
|
||||
public SpringApplication(ResourceLoader resourceLoader, Object... sources) {
|
||||
Assert.notEmpty(sources, "Sources must not be empty");
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.sources = sources;
|
||||
addSources(sources);
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void addSources(Object[] sources) {
|
||||
if (sources == null) {
|
||||
return;
|
||||
}
|
||||
for (Object source : sources) {
|
||||
this.sources.add(source);
|
||||
}
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
this.webEnvironment = deduceWebEnvironment();
|
||||
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
|
||||
|
@ -228,6 +234,22 @@ public class SpringApplication {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic main that can be used to launch an application.
|
||||
*
|
||||
* @param args command line arguments
|
||||
* @see SpringApplication#run(Object[], String[])
|
||||
* @see SpringApplication#run(Object, String...)
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(new Object[0], args);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class EmptyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the Spring application, creating and refreshing a new
|
||||
* {@link ApplicationContext}.
|
||||
|
@ -235,29 +257,30 @@ public class SpringApplication {
|
|||
* @return a running {@link ApplicationContext}
|
||||
*/
|
||||
public ApplicationContext run(String... args) {
|
||||
applySpringApplicationInitializers();
|
||||
applySpringApplicationInitializers(args);
|
||||
Assert.notEmpty(this.sources, "Sources must not be empty");
|
||||
if (this.showBanner) {
|
||||
printBanner();
|
||||
}
|
||||
ApplicationContext context = createApplicationContext();
|
||||
postProcessApplicationContext(context);
|
||||
addPropertySources(context, args);
|
||||
if (context instanceof ConfigurableApplicationContext) {
|
||||
applyInitializers((ConfigurableApplicationContext) context);
|
||||
}
|
||||
if (this.logStartupInfo) {
|
||||
logStartupInfo();
|
||||
}
|
||||
load(context, this.sources);
|
||||
load(context, this.sources.toArray(new Object[this.sources.size()]));
|
||||
refresh(context);
|
||||
runCommandLineRunners(context, args);
|
||||
return context;
|
||||
}
|
||||
|
||||
private void applySpringApplicationInitializers() {
|
||||
private void applySpringApplicationInitializers(String[] args) {
|
||||
args = StringUtils.mergeStringArrays(this.defaultCommandLineArgs, args);
|
||||
for (ApplicationContextInitializer<?> initializer : this.initializers) {
|
||||
if (initializer instanceof SpringApplicationInitializer) {
|
||||
((SpringApplicationInitializer) initializer).initialize(this);
|
||||
((SpringApplicationInitializer) initializer).initialize(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -289,6 +312,7 @@ public class SpringApplication {
|
|||
|
||||
protected void logStartupInfo() {
|
||||
new StartupInfoLogger(this.mainApplicationClass).log(getApplicationLog());
|
||||
getApplicationLog().info("Sources: " + this.sources);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -358,88 +382,6 @@ public class SpringApplication {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any {@link PropertySource}s to the application context environment.
|
||||
* @param context the application context
|
||||
* @param args run arguments
|
||||
*/
|
||||
protected void addPropertySources(ApplicationContext context, String[] args) {
|
||||
Environment environment = context.getEnvironment();
|
||||
if (environment instanceof ConfigurableEnvironment) {
|
||||
ConfigurableEnvironment configurable = (ConfigurableEnvironment) environment;
|
||||
if (this.addCommandLineProperties) {
|
||||
// Don't use SimpleCommandLinePropertySource (SPR-10579)
|
||||
PropertySource<?> propertySource = new MapPropertySource(
|
||||
"commandLineArgs", 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 Map<String, Object> mergeCommandLineArgs(String[] defaults, String[] args) {
|
||||
|
||||
if (defaults == null) {
|
||||
defaults = new String[0];
|
||||
}
|
||||
|
||||
List<String> nonopts = new ArrayList<String>();
|
||||
Map<String, Object> options = new LinkedHashMap<String, Object>();
|
||||
|
||||
for (String arg : defaults) {
|
||||
if (isOptionArg(arg)) {
|
||||
addOptionArg(options, arg);
|
||||
}
|
||||
else {
|
||||
nonopts.add(arg);
|
||||
}
|
||||
}
|
||||
for (String arg : args) {
|
||||
if (isOptionArg(arg)) {
|
||||
addOptionArg(options, arg);
|
||||
}
|
||||
else if (!nonopts.contains(arg)) {
|
||||
nonopts.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
for (String key : nonopts) {
|
||||
options.put(key, "");
|
||||
}
|
||||
|
||||
return options;
|
||||
|
||||
}
|
||||
|
||||
private boolean isOptionArg(String arg) {
|
||||
return arg.startsWith("--");
|
||||
}
|
||||
|
||||
private void addOptionArg(Map<String, Object> map, String arg) {
|
||||
String optionText = arg.substring(2, arg.length());
|
||||
String optionName;
|
||||
String optionValue = "";
|
||||
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
|
||||
|
@ -552,6 +494,13 @@ public class SpringApplication {
|
|||
this.addCommandLineProperties = addCommandLineProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the addCommandLineProperties
|
||||
*/
|
||||
public boolean isAddCommandLineProperties() {
|
||||
return this.addCommandLineProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set some default command line arguments which can be overridden by those passed
|
||||
* into the run methods.
|
||||
|
@ -571,12 +520,45 @@ public class SpringApplication {
|
|||
|
||||
/**
|
||||
* Sets the underlying environment that should be used when loading.
|
||||
*
|
||||
* @param environment the environment
|
||||
*/
|
||||
public void setEnvironment(ConfigurableEnvironment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* The environment that will be used to create the application context (can be null in
|
||||
* which case a default will be provided).
|
||||
*
|
||||
* @return the environment
|
||||
*/
|
||||
public ConfigurableEnvironment getEnvironment() {
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* The sources that will be used to create an ApplicationContext if this application
|
||||
* {@link #run(String...)} is called.
|
||||
*
|
||||
* @return the sources
|
||||
*/
|
||||
public Set<Object> getSources() {
|
||||
return this.sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* The sources that will be used to create an ApplicationContext. A valid source is
|
||||
* one of: a class, class name, package, package name, or an XML resource location.
|
||||
* Can also be set using contructors and static convenience methods (e.g.
|
||||
* {@link #run(Object[], String[])}).
|
||||
*
|
||||
* @param sources the sources to set
|
||||
*/
|
||||
public void setSources(Set<Object> sources) {
|
||||
this.sources = sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ResourceLoader} that should be used when loading resources.
|
||||
* @param resourceLoader the resource loader
|
||||
|
@ -660,21 +642,6 @@ public class SpringApplication {
|
|||
return new SpringApplication(sources).run(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static helper that can be used to run a {@link SpringApplication} from a script
|
||||
* using the specified sources with default settings. This method is useful when
|
||||
* calling this calls from a script environment that will not have a single main
|
||||
* application class.
|
||||
* @param sources the sources to load
|
||||
* @param args the application arguments (usually passed from a Java main method)
|
||||
* @return the running {@link ApplicationContext}
|
||||
*/
|
||||
public static ApplicationContext runFromScript(Object[] sources, String[] args) {
|
||||
SpringApplication application = new SpringApplication(sources);
|
||||
application.setMainApplicationClass(null);
|
||||
return application.run(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static helper that can be used to exit a {@link SpringApplication} and obtain a
|
||||
* code indicating success (0) or otherwise. Does not throw exceptions but should
|
||||
|
|
|
@ -25,9 +25,10 @@ package org.springframework.bootstrap;
|
|||
public interface SpringApplicationInitializer {
|
||||
|
||||
/**
|
||||
* Initialize the applcation
|
||||
* Initialize the application
|
||||
* @param springApplication the spring application.
|
||||
* @param args the args provided on command line by caller
|
||||
*/
|
||||
void initialize(SpringApplication springApplication);
|
||||
void initialize(SpringApplication springApplication, String[] args);
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.springframework.bootstrap.config.YamlProcessor.MatchStatus;
|
|||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public final class DefaultProfileDocumentMatcher implements DocumentMatcher {
|
||||
public class DefaultProfileDocumentMatcher implements DocumentMatcher {
|
||||
|
||||
@Override
|
||||
public MatchStatus matches(Properties properties) {
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.springframework.bootstrap.config;
|
|||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
@ -38,9 +37,9 @@ public class PropertiesPropertySourceLoader implements PropertySourceLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PropertySource<?> load(Resource resource, Environment environment) {
|
||||
public PropertySource<?> load(Resource resource) {
|
||||
try {
|
||||
Properties properties = loadProperties(resource, environment);
|
||||
Properties properties = loadProperties(resource);
|
||||
return new PropertiesPropertySource(resource.getDescription(), properties);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
|
@ -49,8 +48,7 @@ public class PropertiesPropertySourceLoader implements PropertySourceLoader {
|
|||
}
|
||||
}
|
||||
|
||||
protected Properties loadProperties(Resource resource, Environment environment)
|
||||
throws IOException {
|
||||
protected Properties loadProperties(Resource resource) throws IOException {
|
||||
return PropertiesLoaderUtils.loadProperties(resource);
|
||||
}
|
||||
}
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.springframework.bootstrap.config;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
|
@ -37,6 +36,6 @@ public interface PropertySourceLoader {
|
|||
* Load the resource into a property source.
|
||||
* @return a property source
|
||||
*/
|
||||
PropertySource<?> load(Resource resource, Environment environment);
|
||||
PropertySource<?> load(Resource resource);
|
||||
|
||||
}
|
|
@ -16,15 +16,18 @@
|
|||
|
||||
package org.springframework.bootstrap.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher;
|
||||
import org.springframework.bootstrap.config.YamlProcessor.MatchStatus;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link DocumentMatcher} backed by {@link Environment#getActiveProfiles()}.
|
||||
* {@link DocumentMatcher} backed by {@link Environment#getActiveProfiles()}. A YAML
|
||||
* document matches if it contains an element "spring.profiles" (a comma-separated list)
|
||||
* and one of the profiles is in the active list.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
|
@ -32,20 +35,20 @@ public class SpringProfileDocumentMatcher implements DocumentMatcher {
|
|||
|
||||
private static final String[] DEFAULT_PROFILES = new String[] { "default" };
|
||||
|
||||
private final Environment environment;
|
||||
private String[] activeProfiles = new String[0];
|
||||
|
||||
/**
|
||||
* Create a new {@link SpringProfileDocumentMatcher} instance.
|
||||
* @param environment the environment
|
||||
*/
|
||||
public SpringProfileDocumentMatcher(Environment environment) {
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
this.environment = environment;
|
||||
public void addActiveProfiles(String... profiles) {
|
||||
LinkedHashSet<String> set = new LinkedHashSet<String>(
|
||||
Arrays.asList(this.activeProfiles));
|
||||
for (String profile : profiles) {
|
||||
set.add(profile);
|
||||
}
|
||||
this.activeProfiles = set.toArray(new String[set.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MatchStatus matches(Properties properties) {
|
||||
String[] profiles = this.environment.getActiveProfiles();
|
||||
String[] profiles = this.activeProfiles;
|
||||
if (profiles.length == 0) {
|
||||
profiles = DEFAULT_PROFILES;
|
||||
}
|
||||
|
|
|
@ -20,11 +20,13 @@ import java.io.IOException;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.bootstrap.config.YamlProcessor.MatchStatus;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Strategy to load '.yml' files into a {@link PropertySource}.
|
||||
|
@ -49,8 +51,7 @@ public class YamlPropertySourceLoader extends PropertiesPropertySourceLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Properties loadProperties(final Resource resource,
|
||||
final Environment environment) throws IOException {
|
||||
protected Properties loadProperties(final Resource resource) throws IOException {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
if (this.matchers != null && !this.matchers.isEmpty()) {
|
||||
factory.setMatchDefault(false);
|
||||
|
@ -74,13 +75,31 @@ public class YamlPropertySourceLoader extends PropertiesPropertySourceLoader {
|
|||
* which have an explicit "spring.profiles.active" value in the current active
|
||||
* profiles.
|
||||
*
|
||||
* @param activeProfiles the active profiles to match independent of file contents
|
||||
*
|
||||
* @return a property source loader
|
||||
*/
|
||||
public static YamlPropertySourceLoader springProfileAwareLoader(
|
||||
Environment environment) {
|
||||
return new YamlPropertySourceLoader(
|
||||
new SpringProfileDocumentMatcher(environment),
|
||||
new DefaultProfileDocumentMatcher());
|
||||
String[] activeProfiles) {
|
||||
final SpringProfileDocumentMatcher matcher = new SpringProfileDocumentMatcher();
|
||||
for (String profile : activeProfiles) {
|
||||
matcher.addActiveProfiles(profile);
|
||||
}
|
||||
return new YamlPropertySourceLoader(matcher, new DefaultProfileDocumentMatcher() {
|
||||
@Override
|
||||
public MatchStatus matches(Properties properties) {
|
||||
MatchStatus result = super.matches(properties);
|
||||
if (result == MatchStatus.FOUND) {
|
||||
Set<String> profiles = StringUtils.commaDelimitedListToSet(properties
|
||||
.getProperty("spring.profiles.active", ""));
|
||||
for (String profile : profiles) {
|
||||
// allow document with no profile to set the active one
|
||||
matcher.addActiveProfiles(profile);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -17,20 +17,35 @@
|
|||
package org.springframework.bootstrap.context.initializer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.bootstrap.config.SpringProfileDocumentMatcher;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.bootstrap.SpringApplication;
|
||||
import org.springframework.bootstrap.SpringApplicationInitializer;
|
||||
import org.springframework.bootstrap.bind.PropertySourcesPropertyValues;
|
||||
import org.springframework.bootstrap.bind.RelaxedDataBinder;
|
||||
import org.springframework.bootstrap.config.PropertiesPropertySourceLoader;
|
||||
import org.springframework.bootstrap.config.PropertySourceLoader;
|
||||
import org.springframework.bootstrap.config.YamlPropertySourceLoader;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.env.CommandLinePropertySource;
|
||||
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.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
@ -44,6 +59,7 @@ import org.springframework.util.StringUtils;
|
|||
* <li>classpath:config/</li>
|
||||
* <li>file:./config/:</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Alternative locations and names can be specified using
|
||||
* {@link #setSearchLocations(String[])} and {@link #setName(String)}.
|
||||
|
@ -62,7 +78,8 @@ import org.springframework.util.StringUtils;
|
|||
* @author Phillip Webb
|
||||
*/
|
||||
public class ConfigFileApplicationContextInitializer implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>,
|
||||
SpringApplicationInitializer, Ordered {
|
||||
|
||||
private static final String LOCATION_VARIABLE = "${spring.config.location}";
|
||||
|
||||
|
@ -73,24 +90,76 @@ public class ConfigFileApplicationContextInitializer implements
|
|||
|
||||
private int order = Integer.MIN_VALUE + 10;
|
||||
|
||||
private Map<String, PropertySource<?>> cached = new HashMap<String, PropertySource<?>>();
|
||||
|
||||
private ConversionService conversionService = new DefaultConversionService();
|
||||
|
||||
/**
|
||||
* Creates a property source from the command line, loads additional external
|
||||
* configuration, and then binds it to the application. This makes it possible to set
|
||||
* things dynamically, like the sources ("spring.main.sources" - a CSV list) the flag
|
||||
* to indicate a web environment ("spring.main.web_environment=true") or the flag to
|
||||
* switch off the banner ("spring.main.show_banner=false").
|
||||
*/
|
||||
@Override
|
||||
public void initialize(SpringApplication application, String[] args) {
|
||||
|
||||
if (application.isAddCommandLineProperties()) {
|
||||
this.cached.put(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
|
||||
new MapPropertySource(
|
||||
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
|
||||
extractCommandLineArgs(args)));
|
||||
}
|
||||
|
||||
ConfigurableEnvironment environment = application.getEnvironment();
|
||||
if (environment == null) {
|
||||
environment = new StandardEnvironment();
|
||||
}
|
||||
load(environment, new DefaultResourceLoader());
|
||||
|
||||
// Set bean properties from command line args parameters.
|
||||
PropertyValues pvs = new PropertySourcesPropertyValues(
|
||||
environment.getPropertySources());
|
||||
RelaxedDataBinder binder = new RelaxedDataBinder(application, "spring.main");
|
||||
binder.setConversionService(this.conversionService);
|
||||
binder.bind(pvs);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
load(applicationContext.getEnvironment(), applicationContext);
|
||||
}
|
||||
|
||||
private void load(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
|
||||
|
||||
List<String> candidates = getCandidateLocations();
|
||||
|
||||
if (this.cached
|
||||
.containsKey(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
|
||||
environment
|
||||
.getPropertySources()
|
||||
.addFirst(
|
||||
this.cached
|
||||
.get(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME));
|
||||
}
|
||||
|
||||
// Initial load allows profiles to be activated
|
||||
for (String candidate : candidates) {
|
||||
load(applicationContext, candidate, null);
|
||||
load(environment, resourceLoader, candidate, null);
|
||||
}
|
||||
|
||||
// Second load for specific profiles
|
||||
for (String profile : applicationContext.getEnvironment().getActiveProfiles()) {
|
||||
for (String profile : environment.getActiveProfiles()) {
|
||||
for (String candidate : candidates) {
|
||||
load(applicationContext, candidate, profile);
|
||||
load(environment, resourceLoader, candidate, profile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private List<String> getCandidateLocations() {
|
||||
|
||||
List<String> candidates = new ArrayList<String>();
|
||||
for (String searchLocation : this.searchLocations) {
|
||||
for (String extension : new String[] { ".properties", ".yml" }) {
|
||||
|
@ -100,38 +169,118 @@ public class ConfigFileApplicationContextInitializer implements
|
|||
}
|
||||
candidates.add(LOCATION_VARIABLE);
|
||||
return candidates;
|
||||
|
||||
}
|
||||
|
||||
private void load(ConfigurableApplicationContext applicationContext, String location,
|
||||
String profile) {
|
||||
private void load(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
|
||||
String location, String profile) {
|
||||
|
||||
ConfigurableEnvironment environment = applicationContext.getEnvironment();
|
||||
location = environment.resolvePlaceholders(location);
|
||||
String suffix = "." + StringUtils.getFilenameExtension(location);
|
||||
|
||||
if (StringUtils.hasLength(profile)) {
|
||||
location = location.replace(suffix, "-" + profile + suffix);
|
||||
}
|
||||
PropertySourceLoader[] loaders = {
|
||||
new PropertiesPropertySourceLoader(),
|
||||
new YamlPropertySourceLoader(new SpringProfileDocumentMatcher(environment),
|
||||
new ProfileSettingDocumentMatcher(environment)) };
|
||||
for (PropertySourceLoader loader : loaders) {
|
||||
Resource resource = applicationContext.getResource(location);
|
||||
if (resource != null && resource.exists() && loader.supports(resource)) {
|
||||
PropertySource<?> propertySource = loader.load(resource, environment);
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
if (propertySources
|
||||
.contains(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
|
||||
propertySources.addAfter(
|
||||
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
|
||||
propertySource);
|
||||
|
||||
} else {
|
||||
propertySources.addFirst(propertySource);
|
||||
}
|
||||
return;
|
||||
List<PropertySourceLoader> loaders = new ArrayList<PropertySourceLoader>();
|
||||
loaders.add(new PropertiesPropertySourceLoader());
|
||||
if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
|
||||
loaders.add(YamlPropertySourceLoader.springProfileAwareLoader(environment
|
||||
.getActiveProfiles()));
|
||||
}
|
||||
|
||||
Resource resource = resourceLoader.getResource(location);
|
||||
PropertySource<?> propertySource = getPropertySource(resource, loaders);
|
||||
if (propertySource == null) {
|
||||
return;
|
||||
}
|
||||
if (propertySource.containsProperty("spring.profiles.active")) {
|
||||
Set<String> profiles = StringUtils.commaDelimitedListToSet(propertySource
|
||||
.getProperty("spring.profiles.active").toString());
|
||||
for (String active : profiles) {
|
||||
// allow document with no profile to set the active one
|
||||
environment.addActiveProfile(active);
|
||||
}
|
||||
|
||||
}
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
if (propertySources
|
||||
.contains(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
|
||||
propertySources.addAfter(
|
||||
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
|
||||
propertySource);
|
||||
|
||||
}
|
||||
else {
|
||||
propertySources.addFirst(propertySource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private PropertySource<?> getPropertySource(Resource resource,
|
||||
List<PropertySourceLoader> loaders) {
|
||||
String key = resource.getDescription();
|
||||
if (this.cached.containsKey(key)) {
|
||||
return this.cached.get(key);
|
||||
}
|
||||
for (PropertySourceLoader loader : loaders) {
|
||||
if (resource != null && resource.exists() && loader.supports(resource)) {
|
||||
PropertySource<?> propertySource = loader.load(resource);
|
||||
this.cached.put(key, propertySource);
|
||||
return propertySource;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two sets of command lines, the defaults and the ones passed in at run time.
|
||||
*
|
||||
* @param args the ones passed in at runtime
|
||||
* @return a new command line
|
||||
*/
|
||||
protected Map<String, Object> extractCommandLineArgs(String[] args) {
|
||||
|
||||
List<String> nonopts = new ArrayList<String>();
|
||||
Map<String, Object> options = new LinkedHashMap<String, Object>();
|
||||
|
||||
for (String arg : args) {
|
||||
if (isOptionArg(arg)) {
|
||||
addOptionArg(options, arg);
|
||||
}
|
||||
else if (!nonopts.contains(arg)) {
|
||||
nonopts.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
for (String key : nonopts) {
|
||||
options.put(key, "");
|
||||
}
|
||||
|
||||
return options;
|
||||
|
||||
}
|
||||
|
||||
private boolean isOptionArg(String arg) {
|
||||
return arg.startsWith("--");
|
||||
}
|
||||
|
||||
private void addOptionArg(Map<String, Object> map, String arg) {
|
||||
String optionText = arg.substring(2, arg.length());
|
||||
String optionName;
|
||||
String optionValue = "";
|
||||
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);
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
|
|
|
@ -81,7 +81,7 @@ public class LoggingApplicationContextInitializer implements
|
|||
private int order = Integer.MIN_VALUE + 11;
|
||||
|
||||
@Override
|
||||
public void initialize(SpringApplication springApplication) {
|
||||
public void initialize(SpringApplication springApplication, String[] args) {
|
||||
LoggingSystem.get(springApplication.getClass().getClassLoader())
|
||||
.beforeInitialize();
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2013 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.bootstrap.context.initializer;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher;
|
||||
import org.springframework.bootstrap.config.YamlProcessor.MatchStatus;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link DocumentMatcher} that sets the active profile if it finds a document with a
|
||||
* key <code>spring.profiles.active</code>.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ProfileSettingDocumentMatcher implements DocumentMatcher {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
public ProfileSettingDocumentMatcher(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MatchStatus matches(Properties properties) {
|
||||
if (!properties.containsKey("spring.profiles")) {
|
||||
Set<String> profiles = StringUtils.commaDelimitedListToSet(properties
|
||||
.getProperty("spring.profiles.active", ""));
|
||||
if (this.environment instanceof ConfigurableEnvironment) {
|
||||
ConfigurableEnvironment configurable = (ConfigurableEnvironment) this.environment;
|
||||
for (String profile : profiles) {
|
||||
// allow document with no profile to set the active one
|
||||
configurable.addActiveProfile(profile);
|
||||
}
|
||||
}
|
||||
// matches default profile
|
||||
return MatchStatus.FOUND;
|
||||
} else {
|
||||
return MatchStatus.NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -157,15 +157,14 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|||
private PropertySources loadPropertySources(String[] path) {
|
||||
MutablePropertySources propertySources = new MutablePropertySources();
|
||||
PropertySourceLoader[] loaders = { new PropertiesPropertySourceLoader(),
|
||||
YamlPropertySourceLoader.springProfileAwareLoader(this.environment) };
|
||||
YamlPropertySourceLoader.springProfileAwareLoader(environment.getActiveProfiles()) };
|
||||
for (String location : path) {
|
||||
location = this.environment.resolvePlaceholders(location);
|
||||
Resource resource = this.resourceLoader.getResource(location);
|
||||
if (resource != null && resource.exists()) {
|
||||
for (PropertySourceLoader loader : loaders) {
|
||||
if (loader.supports(resource)) {
|
||||
PropertySource<?> propertySource = loader.load(resource,
|
||||
this.environment);
|
||||
PropertySource<?> propertySource = loader.load(resource);
|
||||
propertySources.addFirst(propertySource);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-/tmp/}spring.log}"/>
|
||||
<property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${PID:- } --- %-40.40logger{39} : %m [%t]%n%wex"/>
|
||||
<property name="CONSOLE_LOG_PATTERN" value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m %clr([%t]){faint}%n%wex"/>
|
||||
<property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${PID:- } [%t] --- %-40.40logger{39} : %m%n%wex"/>
|
||||
<property name="CONSOLE_LOG_PATTERN" value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wex"/>
|
||||
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.bootstrap.logging.logback.ColorConverter" />
|
||||
<conversionRule conversionWord="wex" converterClass="org.springframework.bootstrap.logging.logback.WhitespaceThrowableProxyConverter" />
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2012-2013 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.bootstrap;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringApplication} main method.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
public class SimpleMainTests {
|
||||
|
||||
private PrintStream savedOutput;
|
||||
private ByteArrayOutputStream output;
|
||||
|
||||
@Before
|
||||
public void open() {
|
||||
this.savedOutput = System.out;
|
||||
this.output = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(this.output));
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
System.setOut(this.savedOutput);
|
||||
System.out.println(getOutput());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void emptyApplicationContext() throws Exception {
|
||||
SpringApplication.main(getArgs());
|
||||
assertTrue(getOutput().contains("Pre-instantiating singletons"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basePackageScan() throws Exception {
|
||||
SpringApplication.main(getArgs(ClassUtils.getPackageName(getClass())
|
||||
+ ".sampleconfig"));
|
||||
assertTrue(getOutput().contains("Pre-instantiating singletons"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configClassContext() throws Exception {
|
||||
SpringApplication.main(getArgs(getClass().getName()));
|
||||
assertTrue(getOutput().contains("Pre-instantiating singletons"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlContext() throws Exception {
|
||||
SpringApplication.main(getArgs("org/springframework/bootstrap/sample-beans.xml"));
|
||||
assertTrue(getOutput().contains("Pre-instantiating singletons"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mixedContext() throws Exception {
|
||||
SpringApplication.main(getArgs(getClass().getName(),
|
||||
"org/springframework/bootstrap/sample-beans.xml"));
|
||||
assertTrue(getOutput().contains("Pre-instantiating singletons"));
|
||||
}
|
||||
|
||||
private String[] getArgs(String... args) {
|
||||
List<String> list = new ArrayList<String>(Arrays.asList(
|
||||
"--spring.main.webEnvironment=false", "--spring.main.showBanner=false"));
|
||||
if (args.length > 0) {
|
||||
list.add("--spring.main.sources="
|
||||
+ StringUtils.arrayToCommaDelimitedString(args));
|
||||
}
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
|
||||
private String getOutput() {
|
||||
return this.output.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -26,10 +26,6 @@ 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.BeanDefinitionLoader;
|
||||
import org.springframework.bootstrap.CommandLineRunner;
|
||||
import org.springframework.bootstrap.ExitCodeGenerator;
|
||||
import org.springframework.bootstrap.SpringApplication;
|
||||
import org.springframework.bootstrap.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
|
||||
import org.springframework.bootstrap.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -94,14 +90,14 @@ public class SpringApplicationTests {
|
|||
public void sourcesMustNotBeNull() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Sources must not be empty");
|
||||
new SpringApplication((Object[]) null);
|
||||
new SpringApplication((Object[]) null).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sourcesMustNotBeEmpty() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Sources must not be empty");
|
||||
new SpringApplication();
|
||||
new SpringApplication().run();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -270,7 +266,7 @@ public class SpringApplicationTests {
|
|||
application.setWebEnvironment(false);
|
||||
application.setUseMockLoader(true);
|
||||
application.run();
|
||||
assertThat(application.getSources(), equalTo(sources));
|
||||
assertThat(application.getSources().toArray(), equalTo(sources));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -341,8 +337,6 @@ public class SpringApplicationTests {
|
|||
|
||||
private boolean useMockLoader;
|
||||
|
||||
private Object[] sources;
|
||||
|
||||
public TestSpringApplication(Object... sources) {
|
||||
super(sources);
|
||||
}
|
||||
|
@ -358,7 +352,6 @@ public class SpringApplicationTests {
|
|||
@Override
|
||||
protected BeanDefinitionLoader createBeanDefinitionLoader(
|
||||
BeanDefinitionRegistry registry, Object[] sources) {
|
||||
this.sources = sources;
|
||||
if (this.useMockLoader) {
|
||||
this.loader = mock(BeanDefinitionLoader.class);
|
||||
}
|
||||
|
@ -372,9 +365,6 @@ public class SpringApplicationTests {
|
|||
return this.loader;
|
||||
}
|
||||
|
||||
public Object[] getSources() {
|
||||
return this.sources;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
|
|
@ -20,11 +20,15 @@ import java.lang.annotation.Documented;
|
|||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.ConstraintValidator;
|
||||
|
@ -36,7 +40,7 @@ import org.junit.Rule;
|
|||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.bootstrap.bind.RelaxedDataBinder;
|
||||
import org.springframework.beans.NotWritablePropertyException;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
|
@ -119,20 +123,50 @@ public class RelaxedDataBinderTests {
|
|||
@Test
|
||||
public void testBindNestedList() throws Exception {
|
||||
TargetWithNestedList target = new TargetWithNestedList();
|
||||
bind(target, "nested: bar,foo");
|
||||
bind(target, "nested[0]: bar");
|
||||
bind(target, "nested[1]: foo");
|
||||
bind(target, "nested[0]: bar\nnested[1]: foo");
|
||||
assertEquals("[bar, foo]", target.getNested().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNestedListCommaDelimitedONly() throws Exception {
|
||||
public void testBindNestedListCommaDelimitedOnly() throws Exception {
|
||||
TargetWithNestedList target = new TargetWithNestedList();
|
||||
this.conversionService = new DefaultConversionService();
|
||||
bind(target, "nested: bar,foo");
|
||||
assertEquals("[bar, foo]", target.getNested().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNestedSetCommaDelimitedOnly() throws Exception {
|
||||
TargetWithNestedSet target = new TargetWithNestedSet();
|
||||
this.conversionService = new DefaultConversionService();
|
||||
bind(target, "nested: bar,foo");
|
||||
assertEquals("[bar, foo]", target.getNested().toString());
|
||||
}
|
||||
|
||||
@Test(expected = NotWritablePropertyException.class)
|
||||
public void testBindNestedReadOnlyListCommaSeparated() throws Exception {
|
||||
TargetWithReadOnlyNestedList target = new TargetWithReadOnlyNestedList();
|
||||
this.conversionService = new DefaultConversionService();
|
||||
bind(target, "nested: bar,foo");
|
||||
assertEquals("[bar, foo]", target.getNested().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNestedReadOnlyListIndexed() throws Exception {
|
||||
TargetWithReadOnlyNestedList target = new TargetWithReadOnlyNestedList();
|
||||
this.conversionService = new DefaultConversionService();
|
||||
bind(target, "nested[0]: bar\nnested[1]:foo");
|
||||
assertEquals("[bar, foo]", target.getNested().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNestedReadOnlyCollectionIndexed() throws Exception {
|
||||
TargetWithReadOnlyNestedCollection target = new TargetWithReadOnlyNestedCollection();
|
||||
this.conversionService = new DefaultConversionService();
|
||||
bind(target, "nested[0]: bar\nnested[1]:foo");
|
||||
assertEquals("[bar, foo]", target.getNested().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNestedMap() throws Exception {
|
||||
TargetWithNestedMap target = new TargetWithNestedMap();
|
||||
|
@ -306,6 +340,34 @@ public class RelaxedDataBinderTests {
|
|||
}
|
||||
}
|
||||
|
||||
public static class TargetWithReadOnlyNestedList {
|
||||
private List<String> nested = new ArrayList<String>();
|
||||
|
||||
public List<String> getNested() {
|
||||
return this.nested;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TargetWithReadOnlyNestedCollection {
|
||||
private Collection<String> nested = new ArrayList<String>();
|
||||
|
||||
public Collection<String> getNested() {
|
||||
return this.nested;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TargetWithNestedSet {
|
||||
private Set<String> nested = new LinkedHashSet<String>();
|
||||
|
||||
public Set<String> getNested() {
|
||||
return this.nested;
|
||||
}
|
||||
|
||||
public void setNested(Set<String> nested) {
|
||||
this.nested = nested;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TargetWithNestedObject {
|
||||
private VanillaTarget nested;
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sample config for tests
|
||||
*/
|
||||
package org.springframework.bootstrap.sampleconfig;
|
||||
|
|
@ -0,0 +1 @@
|
|||
main.sources: org.springframework.bootstrap.main.DispatcherMainTests
|
Loading…
Reference in New Issue