From d4ef523e6419b7dd8d0dcbb358f479aceed544c0 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Tue, 25 Apr 2017 13:23:08 -0700 Subject: [PATCH] Use new configuration properties in spring-boot Update `spring-boot` to use the new configuration properties support. See gh-9000 --- .../org/springframework/boot/ImageBanner.java | 16 +- .../boot/SpringApplication.java | 22 +-- .../FileEncodingApplicationListener.java | 44 ++--- .../config/AnsiOutputApplicationListener.java | 26 ++- .../config/ConfigFileApplicationListener.java | 120 ++++-------- .../logging/LoggingApplicationListener.java | 25 +-- ...urationPropertiesBindingPostProcessor.java | 176 ++++++++---------- .../boot/logging/LoggingSystemProperties.java | 34 +++- .../logback/DefaultLogbackConfiguration.java | 18 +- .../logging/logback/SpringPropertyAction.java | 17 +- .../boot/system/ApplicationPidFileWriter.java | 5 +- .../yaml/SpringProfileDocumentMatcher.java | 20 +- .../FileEncodingApplicationListenerTests.java | 4 +- .../ConfigFileApplicationListenerTests.java | 6 +- .../LoggingApplicationListenerTests.java | 5 +- ...onPropertiesBindingPostProcessorTests.java | 103 ++++++---- .../EnableConfigurationPropertiesTests.java | 55 +----- .../SpringBootJoranConfiguratorTests.java | 4 +- .../SpringProfileDocumentMatcherTests.java | 4 +- 19 files changed, 321 insertions(+), 383 deletions(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/ImageBanner.java b/spring-boot/src/main/java/org/springframework/boot/ImageBanner.java index 75cdfc5ae6..4c8c7fbf09 100644 --- a/spring-boot/src/main/java/org/springframework/boot/ImageBanner.java +++ b/spring-boot/src/main/java/org/springframework/boot/ImageBanner.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -33,9 +33,7 @@ import org.springframework.boot.ansi.AnsiColor; import org.springframework.boot.ansi.AnsiColors; import org.springframework.boot.ansi.AnsiElement; import org.springframework.boot.ansi.AnsiOutput; -import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.core.env.Environment; -import org.springframework.core.env.PropertyResolver; import org.springframework.core.io.Resource; import org.springframework.util.Assert; @@ -45,6 +43,7 @@ import org.springframework.util.Assert; * * @author Craig Burke * @author Phillip Webb + * @author Madhura Bhave * @since 1.4.0 */ public class ImageBanner implements Banner { @@ -92,12 +91,11 @@ public class ImageBanner implements Banner { private void printBanner(Environment environment, PrintStream out) throws IOException { - PropertyResolver properties = new RelaxedPropertyResolver(environment, - "banner.image."); - int width = properties.getProperty("width", Integer.class, 76); - int height = properties.getProperty("height", Integer.class, 0); - int margin = properties.getProperty("margin", Integer.class, 2); - boolean invert = properties.getProperty("invert", Boolean.class, false); + int width = environment.getProperty("banner.image.width", Integer.class, 76); + int height = environment.getProperty("banner.image.height", Integer.class, 0); + int margin = environment.getProperty("banner.image.margin", Integer.class, 2); + boolean invert = environment.getProperty("banner.image.invert", Boolean.class, + false); BufferedImage image = readImage(width, height); printBanner(image, margin, invert, out); } 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 823c77310e..4a8e28c3a9 100644 --- a/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -40,8 +40,8 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.boot.Banner.Mode; -import org.springframework.boot.bind.PropertiesConfigurationFactory; -import org.springframework.boot.bind.RelaxedPropertyResolver; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextInitializer; @@ -55,8 +55,6 @@ 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.convert.ConversionService; -import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.env.CommandLinePropertySource; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; @@ -76,7 +74,6 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StopWatch; import org.springframework.util.StringUtils; -import org.springframework.validation.BindException; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.StandardServletEnvironment; @@ -578,9 +575,8 @@ public class SpringApplication { private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) { if (System.getProperty( CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) { - RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, - "spring.beaninfo."); - Boolean ignore = resolver.getProperty("ignore", Boolean.class, Boolean.TRUE); + Boolean ignore = environment.getProperty("spring.beaninfo.ignore", + Boolean.class, Boolean.TRUE); System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString()); } @@ -591,16 +587,10 @@ public class SpringApplication { * @param environment the environment to bind */ protected void bindToSpringApplication(ConfigurableEnvironment environment) { - PropertiesConfigurationFactory binder = new PropertiesConfigurationFactory<>( - this); - ConversionService conversionService = new DefaultConversionService(); - binder.setTargetName("spring.main"); - binder.setConversionService(conversionService); - binder.setPropertySources(environment.getPropertySources()); try { - binder.bindPropertiesToTarget(); + Binder.get(environment).bind("spring.main", Bindable.ofInstance(this)); } - catch (BindException ex) { + catch (Exception ex) { throw new IllegalStateException("Cannot bind to SpringApplication", ex); } } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/FileEncodingApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/context/FileEncodingApplicationListener.java index 50e3d2d4e9..29f0e43684 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/FileEncodingApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/FileEncodingApplicationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -19,10 +19,10 @@ package org.springframework.boot.context; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; +import org.springframework.core.env.ConfigurableEnvironment; /** * An {@link ApplicationListener} that halts application startup if the system file @@ -42,6 +42,7 @@ import org.springframework.core.Ordered; * character-encoding value (e.g. "en_GB.UTF-8"). * * @author Dave Syer + * @author Madhura Bhave */ public class FileEncodingApplicationListener implements ApplicationListener, Ordered { @@ -56,26 +57,25 @@ public class FileEncodingApplicationListener @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { - RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( - event.getEnvironment(), "spring."); - if (resolver.containsProperty("mandatoryFileEncoding")) { - String encoding = System.getProperty("file.encoding"); - String desired = resolver.getProperty("mandatoryFileEncoding"); - if (encoding != null && !desired.equalsIgnoreCase(encoding)) { - logger.error("System property 'file.encoding' is currently '" + encoding - + "'. It should be '" + desired - + "' (as defined in 'spring.mandatoryFileEncoding')."); - logger.error("Environment variable LANG is '" + System.getenv("LANG") - + "'. You could use a locale setting that matches encoding='" - + desired + "'."); - logger.error("Environment variable LC_ALL is '" + System.getenv("LC_ALL") - + "'. You could use a locale setting that matches encoding='" - + desired + "'."); - throw new IllegalStateException( - "The Java Virtual Machine has not been configured to use the " - + "desired default character encoding (" + desired - + ")."); - } + ConfigurableEnvironment environment = event.getEnvironment(); + if (!environment.containsProperty("spring.mandatory-file-encoding")) { + return; + } + String encoding = System.getProperty("file.encoding"); + String desired = environment.getProperty("spring.mandatory-file-encoding"); + if (encoding != null && !desired.equalsIgnoreCase(encoding)) { + logger.error("System property 'file.encoding' is currently '" + encoding + + "'. It should be '" + desired + + "' (as defined in 'spring.mandatoryFileEncoding')."); + logger.error("Environment variable LANG is '" + System.getenv("LANG") + + "'. You could use a locale setting that matches encoding='" + + desired + "'."); + logger.error("Environment variable LC_ALL is '" + System.getenv("LC_ALL") + + "'. You could use a locale setting that matches encoding='" + + desired + "'."); + throw new IllegalStateException( + "The Java Virtual Machine has not been configured to use the " + + "desired default character encoding (" + desired + ")."); } } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/config/AnsiOutputApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/context/config/AnsiOutputApplicationListener.java index d82f1e2ef0..24bbe98a10 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/config/AnsiOutputApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/config/AnsiOutputApplicationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2017 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. @@ -18,10 +18,11 @@ package org.springframework.boot.context.config; import org.springframework.boot.ansi.AnsiOutput; import org.springframework.boot.ansi.AnsiOutput.Enabled; -import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.boot.context.properties.bind.Binder; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; +import org.springframework.core.env.ConfigurableEnvironment; /** * An {@link ApplicationListener} that configures {@link AnsiOutput} depending on the @@ -29,6 +30,7 @@ import org.springframework.core.Ordered; * values. * * @author Raphael von der Grün + * @author Madhura Bhave * @since 1.2.0 */ public class AnsiOutputApplicationListener @@ -36,23 +38,17 @@ public class AnsiOutputApplicationListener @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { - RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( - event.getEnvironment(), "spring.output.ansi."); - if (resolver.containsProperty("enabled")) { - String enabled = resolver.getProperty("enabled"); - AnsiOutput.setEnabled(Enum.valueOf(Enabled.class, enabled.toUpperCase())); - } - - if (resolver.containsProperty("console-available")) { - AnsiOutput.setConsoleAvailable( - resolver.getProperty("console-available", Boolean.class)); - } + ConfigurableEnvironment environment = event.getEnvironment(); + Binder.get(environment) + .bind("spring.output.ansi.enabled", AnsiOutput.Enabled.class) + .ifBound(AnsiOutput::setEnabled); + AnsiOutput.setConsoleAvailable(environment + .getProperty("spring.output.ansi.console-available", Boolean.class)); } @Override public int getOrder() { - // Apply after ConfigFileApplicationListener has called all - // EnvironmentPostProcessors + // Apply after ConfigFileApplicationListener has called EnvironmentPostProcessors return ConfigFileApplicationListener.DEFAULT_ORDER + 1; } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java index b4120d2544..297ac295f0 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java @@ -34,10 +34,11 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.SpringApplication; -import org.springframework.boot.bind.PropertySourcesPropertyValues; -import org.springframework.boot.bind.RelaxedDataBinder; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.env.EnumerableCompositePropertySource; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.env.PropertySourcesLoader; @@ -90,6 +91,7 @@ import org.springframework.util.StringUtils; * @author Stephane Nicoll * @author Andy Wilkinson * @author Eddú Meléndez + * @author Madhura Bhave */ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, ApplicationListener, Ordered { @@ -256,8 +258,7 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, } private void reorderSources(ConfigurableEnvironment environment) { - ConfigurationPropertySources - .finishAndRelocate(environment.getPropertySources()); + LoadedPropertySources.finishAndRelocate(environment.getPropertySources()); PropertySource defaultProperties = environment.getPropertySources() .remove(DEFAULT_PROPERTIES); if (defaultProperties != null) { @@ -344,11 +345,11 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, } // Any pre-existing active profiles set via property sources (e.g. System // properties) take precedence over those added in config files. - SpringProfiles springProfiles = bindSpringProfiles( - this.environment.getPropertySources()); - Set activeProfiles = new LinkedHashSet<>( - springProfiles.getActiveProfiles()); - activeProfiles.addAll(springProfiles.getIncludeProfiles()); + Set active = getProfiles(this.environment, "spring.profiles.active"); + Set activeProfiles = new LinkedHashSet<>(active); + Set include = getProfiles(this.environment, + "spring.profiles.include"); + activeProfiles.addAll(include); maybeActivateProfiles(activeProfiles); return activeProfiles; } @@ -474,33 +475,34 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, } private void handleProfileProperties(PropertySource propertySource) { - SpringProfiles springProfiles = bindSpringProfiles(propertySource); - maybeActivateProfiles(springProfiles.getActiveProfiles()); - addProfiles(springProfiles.getIncludeProfiles()); - } - - private SpringProfiles bindSpringProfiles(PropertySource propertySource) { MutablePropertySources propertySources = new MutablePropertySources(); propertySources.addFirst(propertySource); - return bindSpringProfiles(propertySources); + Set active = getProfiles(propertySources, "spring.profiles.active"); + Set include = getProfiles(propertySources, + "spring.profiles.include"); + maybeActivateProfiles(active); + addProfiles(include); } - private SpringProfiles bindSpringProfiles(PropertySources propertySources) { - SpringProfiles springProfiles = new SpringProfiles(); - RelaxedDataBinder dataBinder = new RelaxedDataBinder(springProfiles, - "spring.profiles"); - dataBinder.bind(new PropertySourcesPropertyValues(propertySources, false)); - springProfiles.setActive(resolvePlaceholders(springProfiles.getActive())); - springProfiles.setInclude(resolvePlaceholders(springProfiles.getInclude())); - return springProfiles; + private Set getProfiles(ConfigurableEnvironment environment, + String name) { + return getProfiles(environment.getPropertySources(), name); } - private List resolvePlaceholders(List values) { - List resolved = new ArrayList<>(); - for (String value : values) { - resolved.add(this.environment.resolvePlaceholders(value)); + private Set getProfiles(PropertySources sources, String name) { + Binder binder = new Binder(ConfigurationPropertySources.get(sources), + new PropertySourcesPlaceholdersResolver(this.environment)); + return binder.bind(name, String[].class).map(this::asProfileSet) + .orElse(Collections.emptySet()); + } + + private Set asProfileSet(String[] profileNames) { + List profiles = new ArrayList<>(); + for (String profileName : profileNames) { + profiles.add(new Profile(profileName)); } - return resolved; + Collections.reverse(profiles); + return new LinkedHashSet<>(profiles); } private void maybeActivateProfiles(Set profiles) { @@ -601,19 +603,17 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, for (PropertySource item : sources) { reorderedSources.add(item); } - addConfigurationProperties( - new ConfigurationPropertySources(reorderedSources)); + addConfigurationProperties(new LoadedPropertySources(reorderedSources)); } - private void addConfigurationProperties( - ConfigurationPropertySources configurationSources) { + private void addConfigurationProperties(LoadedPropertySources loadedSources) { MutablePropertySources existingSources = this.environment .getPropertySources(); if (existingSources.contains(DEFAULT_PROPERTIES)) { - existingSources.addBefore(DEFAULT_PROPERTIES, configurationSources); + existingSources.addBefore(DEFAULT_PROPERTIES, loadedSources); } else { - existingSources.addLast(configurationSources); + existingSources.addLast(loadedSources); } } @@ -670,14 +670,14 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, * Holds the configuration {@link PropertySource}s as they are loaded can relocate * them once configuration classes have been processed. */ - static class ConfigurationPropertySources + static class LoadedPropertySources extends EnumerablePropertySource>> { private final Collection> sources; private final String[] names; - ConfigurationPropertySources(Collection> sources) { + LoadedPropertySources(Collection> sources) { super(APPLICATION_CONFIGURATION_PROPERTY_SOURCE_NAME, sources); this.sources = sources; List names = new ArrayList<>(); @@ -703,7 +703,7 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, public static void finishAndRelocate(MutablePropertySources propertySources) { String name = APPLICATION_CONFIGURATION_PROPERTY_SOURCE_NAME; - ConfigurationPropertySources removed = (ConfigurationPropertySources) propertySources + LoadedPropertySources removed = (LoadedPropertySources) propertySources .get(name); if (removed != null) { for (PropertySource propertySource : removed.sources) { @@ -729,48 +729,4 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, } - /** - * Holder for {@code spring.profiles} properties. - */ - static final class SpringProfiles { - - private List active = new ArrayList<>(); - - private List include = new ArrayList<>(); - - public List getActive() { - return this.active; - } - - public void setActive(List active) { - this.active = active; - } - - public List getInclude() { - return this.include; - } - - public void setInclude(List include) { - this.include = include; - } - - Set getActiveProfiles() { - return asProfileSet(this.active); - } - - Set getIncludeProfiles() { - return asProfileSet(this.include); - } - - private Set asProfileSet(List profileNames) { - List profiles = new ArrayList<>(); - for (String profileName : profileNames) { - profiles.add(new Profile(profileName)); - } - Collections.reverse(profiles); - return new LinkedHashSet<>(profiles); - } - - } - } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java index 5292b86689..1f52d7282b 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java @@ -16,9 +16,9 @@ package org.springframework.boot.context.logging; +import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; @@ -26,11 +26,12 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.SpringApplication; -import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.event.ApplicationStartingEvent; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggingInitializationContext; @@ -77,11 +78,15 @@ import org.springframework.util.StringUtils; * @author Dave Syer * @author Phillip Webb * @author Andy Wilkinson + * @author Madhura Bhave * @since 2.0.0 * @see LoggingSystem#get(ClassLoader) */ public class LoggingApplicationListener implements GenericApplicationListener { + private static final Bindable> STRING_STRING_MAP = Bindable + .mapOf(String.class, String.class); + /** * The default order for the LoggingApplicationListener. */ @@ -294,20 +299,18 @@ public class LoggingApplicationListener implements GenericApplicationListener { } protected void setLogLevels(LoggingSystem system, Environment environment) { - Map levels = new RelaxedPropertyResolver(environment) - .getSubProperties("logging.level."); - for (Entry entry : levels.entrySet()) { - setLogLevel(system, environment, entry.getKey(), entry.getValue().toString()); + if (!(environment instanceof ConfigurableEnvironment)) { + return; } + Binder binder = Binder.get(environment); + binder.bind("logging.level", STRING_STRING_MAP).orElseGet(Collections::emptyMap) + .forEach((name, level) -> setLogLevel(system, environment, name, level)); } private void setLogLevel(LoggingSystem system, Environment environment, String name, String level) { try { - if (name.equalsIgnoreCase(LoggingSystem.ROOT_LOGGER_NAME)) { - name = null; - } - level = environment.resolvePlaceholders(level); + name = (name.equalsIgnoreCase(LoggingSystem.ROOT_LOGGER_NAME) ? null : name); system.setLogLevel(name, coerceLogLevel(level)); } catch (RuntimeException ex) { @@ -324,7 +327,7 @@ public class LoggingApplicationListener implements GenericApplicationListener { private void registerShutdownHookIfNecessary(Environment environment, LoggingSystem loggingSystem) { - boolean registerShutdownHook = new RelaxedPropertyResolver(environment) + boolean registerShutdownHook = environment .getProperty(REGISTER_SHUTDOWN_HOOK_PROPERTY, Boolean.class, false); if (registerShutdownHook) { Runnable shutdownHandler = loggingSystem.getShutdownHandler(); diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java index c1e358d1df..7d718f7f8e 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java @@ -17,7 +17,6 @@ package org.springframework.boot.context.properties; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; @@ -34,7 +33,15 @@ import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.boot.bind.PropertiesConfigurationFactory; +import org.springframework.boot.context.properties.bind.BindHandler; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver; +import org.springframework.boot.context.properties.bind.handler.IgnoreErrorsBindHandler; +import org.springframework.boot.context.properties.bind.handler.IgnoreNestedPropertiesBindHandler; +import org.springframework.boot.context.properties.bind.handler.NoUnboundElementsBindHandler; +import org.springframework.boot.context.properties.bind.validation.ValidationBindHandler; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.validation.MessageInterpolatorFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -53,13 +60,10 @@ import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySources; import org.springframework.core.env.StandardEnvironment; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; import org.springframework.validation.Errors; import org.springframework.validation.Validator; import org.springframework.validation.annotation.Validated; @@ -73,6 +77,7 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; * @author Phillip Webb * @author Christian Dupuis * @author Stephane Nicoll + * @author Madhura Bhave */ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor, BeanFactoryAware, EnvironmentAware, ApplicationContextAware, InitializingBean, @@ -113,6 +118,10 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc private int order = Ordered.HIGHEST_PRECEDENCE + 1; + private ConfigurationPropertySources configurationSources; + + private Binder binder; + /** * A list of custom converters (in addition to the defaults) to use when converting * properties for binding. @@ -212,6 +221,8 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME, ConversionService.class); } + this.configurationSources = ConfigurationPropertySources + .get(this.propertySources); } @Override @@ -240,18 +251,13 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc private PropertySources deducePropertySources() { PropertySourcesPlaceholderConfigurer configurer = getSinglePropertySourcesPlaceholderConfigurer(); if (configurer != null) { - // Flatten the sources into a single list so they can be iterated - return new FlatPropertySources(configurer.getAppliedPropertySources()); + return configurer.getAppliedPropertySources(); } if (this.environment instanceof ConfigurableEnvironment) { - MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment) - .getPropertySources(); - return new FlatPropertySources(propertySources); + return ((ConfigurableEnvironment) this.environment).getPropertySources(); } - // empty, so not very useful, but fulfils the contract - logger.warn("Unable to obtain PropertySources from " + throw new IllegalStateException("Unable to obtain PropertySources from " + "PropertySourcesPlaceholderConfigurer or Environment"); - return new MutablePropertySources(); } private PropertySourcesPlaceholderConfigurer getSinglePropertySourcesPlaceholderConfigurer() { @@ -287,15 +293,16 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc throws BeansException { ConfigurationProperties annotation = AnnotationUtils .findAnnotation(bean.getClass(), ConfigurationProperties.class); + Object bound = bean; if (annotation != null) { - postProcessBeforeInitialization(bean, beanName, annotation); + bound = postProcessBeforeInitialization(bean, beanName, annotation); } annotation = this.beans.findFactoryAnnotation(beanName, ConfigurationProperties.class); if (annotation != null) { - postProcessBeforeInitialization(bean, beanName, annotation); + bound = postProcessBeforeInitialization(bean, beanName, annotation); } - return bean; + return bound; } @Override @@ -304,35 +311,53 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc return bean; } - private void postProcessBeforeInitialization(Object bean, String beanName, + private Object postProcessBeforeInitialization(Object bean, String beanName, ConfigurationProperties annotation) { - Object target = bean; - PropertiesConfigurationFactory factory = new PropertiesConfigurationFactory<>( - target); - factory.setPropertySources(this.propertySources); - factory.setValidator(determineValidator(bean)); - // If no explicit conversion service is provided we add one so that (at least) - // comma-separated arrays of convertibles can be bound automatically - factory.setConversionService(this.conversionService == null - ? getDefaultConversionService() : this.conversionService); - if (annotation != null) { - factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields()); - factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields()); - factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties()); - if (StringUtils.hasLength(annotation.prefix())) { - factory.setTargetName(annotation.prefix()); - } - } + Binder binder = getBinder(); + Validator validator = determineValidator(bean); + BindHandler handler = getBindHandler(annotation, validator); + Bindable bindable = Bindable.ofInstance(bean); try { - factory.bindPropertiesToTarget(); + binder.bind(annotation.prefix(), bindable, handler); + return bean; } catch (Exception ex) { - String targetClass = ClassUtils.getShortName(target.getClass()); + String targetClass = ClassUtils.getShortName(bean.getClass()); throw new BeanCreationException(beanName, "Could not bind properties to " + targetClass + " (" + getAnnotationDetails(annotation) + ")", ex); } } + private Binder getBinder() { + Binder binder = this.binder; + if (binder == null) { + ConversionService conversionService = this.conversionService; + if (conversionService == null) { + conversionService = getDefaultConversionService(); + } + binder = new Binder(this.configurationSources, + new PropertySourcesPlaceholdersResolver(this.propertySources), + conversionService); + this.binder = binder; + } + return binder; + } + + private ConversionService getDefaultConversionService() { + if (this.defaultConversionService == null) { + DefaultConversionService conversionService = new DefaultConversionService(); + this.applicationContext.getAutowireCapableBeanFactory().autowireBean(this); + for (Converter converter : this.converters) { + conversionService.addConverter(converter); + } + for (GenericConverter genericConverter : this.genericConverters) { + conversionService.addConverter(genericConverter); + } + this.defaultConversionService = conversionService; + } + return this.defaultConversionService; + } + private String getAnnotationDetails(ConfigurationProperties annotation) { if (annotation == null) { return ""; @@ -379,19 +404,22 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc return true; } - private ConversionService getDefaultConversionService() { - if (this.defaultConversionService == null) { - DefaultConversionService conversionService = new DefaultConversionService(); - this.applicationContext.getAutowireCapableBeanFactory().autowireBean(this); - for (Converter converter : this.converters) { - conversionService.addConverter(converter); - } - for (GenericConverter genericConverter : this.genericConverters) { - conversionService.addConverter(genericConverter); - } - this.defaultConversionService = conversionService; + private BindHandler getBindHandler(ConfigurationProperties annotation, + Validator validator) { + BindHandler handler = BindHandler.DEFAULT; + if (annotation.ignoreInvalidFields()) { + handler = new IgnoreErrorsBindHandler(handler); } - return this.defaultConversionService; + if (!annotation.ignoreUnknownFields()) { + handler = new NoUnboundElementsBindHandler(handler); + } + if (annotation.ignoreNestedProperties()) { + handler = new IgnoreNestedPropertiesBindHandler(handler); + } + if (validator != null) { + handler = new ValidationBindHandler(handler, validator); + } + return handler; } /** @@ -465,56 +493,4 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc } - /** - * Convenience class to flatten out a tree of property sources without losing the - * reference to the backing data (which can therefore be updated in the background). - */ - private static class FlatPropertySources implements PropertySources { - - private PropertySources propertySources; - - FlatPropertySources(PropertySources propertySources) { - this.propertySources = propertySources; - } - - @Override - public Iterator> iterator() { - MutablePropertySources result = getFlattened(); - return result.iterator(); - } - - @Override - public boolean contains(String name) { - return get(name) != null; - } - - @Override - public PropertySource get(String name) { - return getFlattened().get(name); - } - - private MutablePropertySources getFlattened() { - MutablePropertySources result = new MutablePropertySources(); - for (PropertySource propertySource : this.propertySources) { - flattenPropertySources(propertySource, result); - } - return result; - } - - private void flattenPropertySources(PropertySource propertySource, - MutablePropertySources result) { - Object source = propertySource.getSource(); - if (source instanceof ConfigurableEnvironment) { - ConfigurableEnvironment environment = (ConfigurableEnvironment) source; - for (PropertySource childSource : environment.getPropertySources()) { - flattenPropertySources(childSource, result); - } - } - else { - result.addLast(propertySource); - } - } - - } - } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java index da7c9e16c1..5794911ff9 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java @@ -17,8 +17,10 @@ package org.springframework.boot.logging; import org.springframework.boot.ApplicationPid; -import org.springframework.boot.bind.RelaxedPropertyResolver; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertyResolver; +import org.springframework.core.env.PropertySourcesPropertyResolver; import org.springframework.util.Assert; /** @@ -26,6 +28,7 @@ import org.springframework.util.Assert; * * @author Andy Wilkinson * @author Phillip Webb + * @author Madhura Bhave * @since 2.0.0 */ public class LoggingSystemProperties { @@ -81,22 +84,33 @@ public class LoggingSystemProperties { } public void apply(LogFile logFile) { - RelaxedPropertyResolver propertyResolver = RelaxedPropertyResolver - .ignoringUnresolvableNestedPlaceholders(this.environment, "logging."); - setSystemProperty(propertyResolver, EXCEPTION_CONVERSION_WORD, + PropertyResolver resolver = getPropertyResolver(); + setSystemProperty(resolver, EXCEPTION_CONVERSION_WORD, "exception-conversion-word"); - setSystemProperty(propertyResolver, CONSOLE_LOG_PATTERN, "pattern.console"); - setSystemProperty(propertyResolver, FILE_LOG_PATTERN, "pattern.file"); - setSystemProperty(propertyResolver, LOG_LEVEL_PATTERN, "pattern.level"); + setSystemProperty(resolver, CONSOLE_LOG_PATTERN, "pattern.console"); + setSystemProperty(resolver, FILE_LOG_PATTERN, "pattern.file"); + setSystemProperty(resolver, LOG_LEVEL_PATTERN, "pattern.level"); setSystemProperty(PID_KEY, new ApplicationPid().toString()); if (logFile != null) { logFile.applyToSystemProperties(); } } - private void setSystemProperty(RelaxedPropertyResolver propertyResolver, - String systemPropertyName, String propertyName) { - setSystemProperty(systemPropertyName, propertyResolver.getProperty(propertyName)); + private PropertyResolver getPropertyResolver() { + if (this.environment instanceof ConfigurableEnvironment) { + PropertyResolver resolver = new PropertySourcesPropertyResolver( + ((ConfigurableEnvironment) this.environment).getPropertySources()); + ((PropertySourcesPropertyResolver) resolver) + .setIgnoreUnresolvableNestedPlaceholders(true); + return resolver; + } + return this.environment; + } + + private void setSystemProperty(PropertyResolver resolver, String systemPropertyName, + String propertyName) { + setSystemProperty(systemPropertyName, + resolver.getProperty("logging." + propertyName)); } private void setSystemProperty(String name, String value) { diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java index ec1451bfb1..17b92c6f24 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java @@ -30,9 +30,9 @@ import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; import ch.qos.logback.core.util.FileSize; import ch.qos.logback.core.util.OptionHelper; -import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LoggingInitializationContext; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertyResolver; import org.springframework.core.env.PropertySourcesPropertyResolver; @@ -44,6 +44,7 @@ import org.springframework.util.ReflectionUtils; * and {@code file-appender.xml} files provided for classic {@code logback.xml} use. * * @author Phillip Webb + * @author Madhura Bhave * @since 1.1.2 */ class DefaultLogbackConfiguration { @@ -72,8 +73,13 @@ class DefaultLogbackConfiguration { if (environment == null) { return new PropertySourcesPropertyResolver(null); } - return RelaxedPropertyResolver.ignoringUnresolvableNestedPlaceholders(environment, - "logging.pattern."); + if (environment instanceof ConfigurableEnvironment) { + PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver( + ((ConfigurableEnvironment) environment).getPropertySources()); + resolver.setIgnoreUnresolvableNestedPlaceholders(true); + return resolver; + } + return environment; } public void apply(LogbackConfigurator config) { @@ -113,7 +119,8 @@ class DefaultLogbackConfiguration { private Appender consoleAppender(LogbackConfigurator config) { ConsoleAppender appender = new ConsoleAppender<>(); PatternLayoutEncoder encoder = new PatternLayoutEncoder(); - String logPattern = this.patterns.getProperty("console", CONSOLE_LOG_PATTERN); + String logPattern = this.patterns.getProperty("logging.pattern.console", + CONSOLE_LOG_PATTERN); encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext())); encoder.setCharset(UTF8); config.start(encoder); @@ -126,7 +133,8 @@ class DefaultLogbackConfiguration { String logFile) { RollingFileAppender appender = new RollingFileAppender<>(); PatternLayoutEncoder encoder = new PatternLayoutEncoder(); - String logPattern = this.patterns.getProperty("file", FILE_LOG_PATTERN); + String logPattern = this.patterns.getProperty("logging.pattern.file", + FILE_LOG_PATTERN); encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext())); appender.setEncoder(encoder); config.start(encoder); diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringPropertyAction.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringPropertyAction.java index 4b9f67251d..ca64626793 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringPropertyAction.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/SpringPropertyAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -24,7 +24,6 @@ import ch.qos.logback.core.joran.spi.InterpretationContext; import ch.qos.logback.core.util.OptionHelper; import org.xml.sax.Attributes; -import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.core.env.Environment; /** @@ -33,6 +32,7 @@ import org.springframework.core.env.Environment; * * @author Phillip Webb * @author Eddú Meléndez + * @author Madhura Bhave */ class SpringPropertyAction extends Action { @@ -47,8 +47,8 @@ class SpringPropertyAction extends Action { } @Override - public void begin(InterpretationContext ic, String elementName, Attributes attributes) - throws ActionException { + public void begin(InterpretationContext context, String elementName, + Attributes attributes) throws ActionException { String name = attributes.getValue(NAME_ATTRIBUTE); String source = attributes.getValue(SOURCE_ATTRIBUTE); Scope scope = ActionUtil.stringToScope(attributes.getValue(SCOPE_ATTRIBUTE)); @@ -57,7 +57,7 @@ class SpringPropertyAction extends Action { addError( "The \"name\" and \"source\" attributes of must be set"); } - ActionUtil.setProperty(ic, name, getValue(source, defaultValue), scope); + ActionUtil.setProperty(context, name, getValue(source, defaultValue), scope); } private String getValue(String source, String defaultValue) { @@ -72,15 +72,14 @@ class SpringPropertyAction extends Action { int lastDot = source.lastIndexOf("."); if (lastDot > 0) { String prefix = source.substring(0, lastDot + 1); - RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( - this.environment, prefix); - return resolver.getProperty(source.substring(lastDot + 1), defaultValue); + return this.environment.getProperty(prefix + source.substring(lastDot + 1), + defaultValue); } return defaultValue; } @Override - public void end(InterpretationContext ic, String name) throws ActionException { + public void end(InterpretationContext context, String name) throws ActionException { } } diff --git a/spring-boot/src/main/java/org/springframework/boot/system/ApplicationPidFileWriter.java b/spring-boot/src/main/java/org/springframework/boot/system/ApplicationPidFileWriter.java index afb0a2f565..726a32a06e 100644 --- a/spring-boot/src/main/java/org/springframework/boot/system/ApplicationPidFileWriter.java +++ b/spring-boot/src/main/java/org/springframework/boot/system/ApplicationPidFileWriter.java @@ -27,7 +27,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.boot.ApplicationPid; -import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -57,6 +56,7 @@ import org.springframework.util.Assert; * @author Dave Syer * @author Phillip Webb * @author Tomasz Przybyla + * @author Madhura Bhave * @since 1.4.0 */ public class ApplicationPidFileWriter @@ -221,8 +221,7 @@ public class ApplicationPidFileWriter if (environment == null) { return null; } - return new RelaxedPropertyResolver(environment, this.prefix) - .getProperty(this.key); + return environment.getProperty(this.prefix + this.key); } private Environment getEnvironment(SpringApplicationEvent event) { diff --git a/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java b/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java index b197ef033e..5a5aa380f1 100644 --- a/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java +++ b/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java @@ -25,14 +25,12 @@ import java.util.List; import java.util.Properties; import java.util.Set; -import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher; import org.springframework.beans.factory.config.YamlProcessor.MatchStatus; -import org.springframework.boot.bind.PropertySourcesPropertyValues; -import org.springframework.boot.bind.RelaxedDataBinder; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; import org.springframework.core.env.Environment; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertiesPropertySource; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -47,6 +45,7 @@ import org.springframework.util.StringUtils; * @author Matt Benson * @author Phillip Webb * @author Andy Wilkinson + * @author Madhura Bhave */ public class SpringProfileDocumentMatcher implements DocumentMatcher { @@ -69,14 +68,9 @@ public class SpringProfileDocumentMatcher implements DocumentMatcher { } protected List extractSpringProfiles(Properties properties) { - SpringProperties springProperties = new SpringProperties(); - MutablePropertySources propertySources = new MutablePropertySources(); - propertySources.addFirst(new PropertiesPropertySource("profiles", properties)); - PropertyValues propertyValues = new PropertySourcesPropertyValues( - propertySources); - new RelaxedDataBinder(springProperties, "spring").bind(propertyValues); - List profiles = springProperties.getProfiles(); - return profiles; + Binder binder = new Binder(new MapConfigurationPropertySource(properties)); + return binder.bind("spring.profiles", Bindable.of(String[].class)) + .map(Arrays::asList).orElse(Collections.emptyList()); } private MatchStatus matches(List profiles) { diff --git a/spring-boot/src/test/java/org/springframework/boot/context/FileEncodingApplicationListenerTests.java b/spring-boot/src/test/java/org/springframework/boot/context/FileEncodingApplicationListenerTests.java index b698a32bd7..415a516238 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/FileEncodingApplicationListenerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/FileEncodingApplicationListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -21,6 +21,7 @@ import org.junit.Test; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.StandardEnvironment; import org.springframework.test.context.support.TestPropertySourceUtils; @@ -43,6 +44,7 @@ public class FileEncodingApplicationListenerTests { public void testIllegalState() { TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, "spring.mandatory_file_encoding=FOO"); + ConfigurationPropertySources.attach(this.environment); this.initializer.onApplicationEvent(this.event); } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java b/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java index aaacefae5d..11aa788a2d 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java @@ -40,7 +40,7 @@ import org.junit.rules.ExpectedException; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; -import org.springframework.boot.context.config.ConfigFileApplicationListener.ConfigurationPropertySources; +import org.springframework.boot.context.config.ConfigFileApplicationListener.LoadedPropertySources; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.env.EnumerableCompositePropertySource; @@ -512,7 +512,7 @@ public class ConfigFileApplicationListenerTests { String property = this.environment.getProperty("my.property"); assertThat(this.environment.getActiveProfiles()).contains("dev"); assertThat(property).isEqualTo("fromdevprofile"); - ConfigurationPropertySources propertySource = (ConfigurationPropertySources) this.environment + LoadedPropertySources propertySource = (LoadedPropertySources) this.environment .getPropertySources() .get(ConfigFileApplicationListener.APPLICATION_CONFIGURATION_PROPERTY_SOURCE_NAME); Collection> sources = propertySource @@ -848,7 +848,7 @@ public class ConfigFileApplicationListenerTests { public boolean matches(ConfigurableEnvironment value) { MutablePropertySources sources = new MutablePropertySources( value.getPropertySources()); - ConfigurationPropertySources.finishAndRelocate(sources); + LoadedPropertySources.finishAndRelocate(sources); return sources.contains(sourceName); } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/logging/LoggingApplicationListenerTests.java b/spring-boot/src/test/java/org/springframework/boot/context/logging/LoggingApplicationListenerTests.java index 5e6222c615..6e413b084e 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/logging/LoggingApplicationListenerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/logging/LoggingApplicationListenerTests.java @@ -41,6 +41,7 @@ import org.springframework.boot.ApplicationPid; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.context.event.ApplicationStartingEvent; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.junit.runner.classpath.ClassPathExclusions; import org.springframework.boot.junit.runner.classpath.ModifiedClassPathRunner; import org.springframework.boot.logging.AbstractLoggingSystem; @@ -56,6 +57,7 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.test.util.ReflectionTestUtils; @@ -72,7 +74,6 @@ import static org.hamcrest.Matchers.not; * @author Stephane Nicoll * @author Ben Hale */ - @RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions("log4j*.jar") public class LoggingApplicationListenerTests { @@ -103,6 +104,8 @@ public class LoggingApplicationListenerTests { multicastEvent(new ApplicationStartingEvent(new SpringApplication(), NO_ARGS)); new File("target/foo.log").delete(); new File(tmpDir() + "/spring.log").delete(); + ConfigurableEnvironment environment = this.context.getEnvironment(); + ConfigurationPropertySources.attach(environment.getPropertySources()); } @After diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java index 5e81e4543f..18838f6c06 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java @@ -34,7 +34,8 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.GenericBeanDefinition; -import org.springframework.boot.bind.RelaxedBindingNotWritablePropertyException; +import org.springframework.boot.context.properties.bind.BindException; +import org.springframework.boot.context.properties.bind.validation.BindValidationException; import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -43,7 +44,6 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.mock.env.MockEnvironment; import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.validation.BindException; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; @@ -59,6 +59,7 @@ import static org.junit.Assert.fail; * @author Christian Dupuis * @author Phillip Webb * @author Stephane Nicoll + * @author Madhura Bhave */ public class ConfigurationPropertiesBindingPostProcessorTests { @@ -83,7 +84,16 @@ public class ConfigurationPropertiesBindingPostProcessorTests { TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, "test.foo=spam"); this.context.register(TestConfigurationWithValidatingSetter.class); - assertBindingFailure(1); + try { + this.context.refresh(); + fail("Expected exception"); + } + catch (BeanCreationException ex) { + BindException bindException = (BindException) ex.getCause(); + assertThat(bindException.getMessage()) + .startsWith("Failed to bind properties under 'test' to " + + PropertyWithValidatingSetter.class.getName()); + } } @Test @@ -97,12 +107,9 @@ public class ConfigurationPropertiesBindingPostProcessorTests { fail("Expected exception"); } catch (BeanCreationException ex) { - RelaxedBindingNotWritablePropertyException bex = (RelaxedBindingNotWritablePropertyException) ex - .getRootCause(); - assertThat(bex.getMessage()) - .startsWith("Failed to bind 'com.example.baz' from '" - + TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME - + "' to 'baz' " + "property on '" + BindException bindException = (BindException) ex.getCause(); + assertThat(bindException.getMessage()) + .startsWith("Failed to bind properties under 'com.example' to " + TestConfiguration.class.getName()); } } @@ -190,9 +197,7 @@ public class ConfigurationPropertiesBindingPostProcessorTests { @Test public void testRelaxedPropertyWithEnum() throws Exception { doEnumTest("test.the-value=FoO"); - doEnumTest("TEST_THE_VALUE=FoO"); doEnumTest("test.THE_VALUE=FoO"); - doEnumTest("test_the_value=FoO"); } private void doEnumTest(String property) { @@ -209,8 +214,6 @@ public class ConfigurationPropertiesBindingPostProcessorTests { public void testRelaxedPropertyWithSetOfEnum() { doEnumSetTest("test.the-values=foo,bar", FooEnum.FOO, FooEnum.BAR); doEnumSetTest("test.the-values=foo", FooEnum.FOO); - doEnumSetTest("TEST_THE_VALUES=FoO", FooEnum.FOO); - doEnumSetTest("test_the_values=BaR,FoO", FooEnum.BAR, FooEnum.FOO); } private void doEnumSetTest(String property, FooEnum... expected) { @@ -266,17 +269,6 @@ public class ConfigurationPropertiesBindingPostProcessorTests { .isEqualTo("word".toCharArray()); } - @Test - public void configurationPropertiesWithArrayExpansion() throws Exception { - this.context = new AnnotationConfigApplicationContext(); - TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, - "test.chars[4]=s"); - this.context.register(PropertyWithCharArrayExpansion.class); - this.context.refresh(); - assertThat(this.context.getBean(PropertyWithCharArrayExpansion.class).getChars()) - .isEqualTo("words".toCharArray()); - } - @Test public void notWritablePropertyException() throws Exception { this.context = new AnnotationConfigApplicationContext(); @@ -294,12 +286,6 @@ public class ConfigurationPropertiesBindingPostProcessorTests { "test.BAR-B-A-Z=testa", "test.BAR-B-A-Z=testb"); } - @Test - public void relaxedPropertyNamesMixed() throws Exception { - testRelaxedPropertyNames("test.FOO_BAR=test2", "test.foo-bar=test1", - "test.BAR-B-A-Z=testb", "test.bar_b_a_z=testa"); - } - private void testRelaxedPropertyNames(String... environment) { this.context = new AnnotationConfigApplicationContext(); TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, @@ -316,7 +302,7 @@ public class ConfigurationPropertiesBindingPostProcessorTests { // gh-3539 this.context = new AnnotationConfigApplicationContext(); TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, - "TEST_NESTED_VALUE=test1"); + "test.nested.value=test1"); this.context.register(PropertyWithNestedValue.class); this.context.refresh(); assertThat(this.context.getBean(PropertyWithNestedValue.class).getNested() @@ -335,6 +321,27 @@ public class ConfigurationPropertiesBindingPostProcessorTests { this.context.refresh(); } + @Test + public void bindWithIgnoreInvalidFieldsAnnotation() { + this.context = new AnnotationConfigApplicationContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, + "com.example.bar=spam"); + this.context.register(TestConfigurationWithIgnoreErrors.class); + this.context.refresh(); + assertThat(this.context.getBean(TestConfigurationWithIgnoreErrors.class).getBar()) + .isEqualTo(0); + } + + @Test + public void bindWithNoIgnoreInvalidFieldsAnnotation() { + this.context = new AnnotationConfigApplicationContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, + "com.example.foo=hello"); + this.context.register(TestConfiguration.class); + this.thrown.expect(BeanCreationException.class); + this.context.refresh(); + } + @Test public void multiplePropertySourcesPlaceholderConfigurer() throws Exception { this.context = new AnnotationConfigApplicationContext(); @@ -361,8 +368,8 @@ public class ConfigurationPropertiesBindingPostProcessorTests { fail("Expected exception"); } catch (BeanCreationException ex) { - BindException bex = (BindException) ex.getRootCause(); - assertThat(bex.getErrorCount()).isEqualTo(errorCount); + assertThat(((BindValidationException) ex.getRootCause()).getValidationErrors() + .getAllErrors().size()).isEqualTo(errorCount); } } @@ -407,6 +414,7 @@ public class ConfigurationPropertiesBindingPostProcessorTests { } @ConfigurationProperties(prefix = "test") + @Validated public static class PropertyWithoutJSR303 implements Validator { private String foo; @@ -469,6 +477,8 @@ public class ConfigurationPropertiesBindingPostProcessorTests { @ConfigurationProperties(prefix = "com.example", ignoreUnknownFields = false) public static class TestConfiguration { + private int foo; + private String bar; public void setBar(String bar) { @@ -479,6 +489,30 @@ public class ConfigurationPropertiesBindingPostProcessorTests { return this.bar; } + public int getFoo() { + return this.foo; + } + + public void setFoo(int foo) { + this.foo = foo; + } + } + + @Configuration + @EnableConfigurationProperties + @ConfigurationProperties(prefix = "com.example", ignoreInvalidFields = true) + public static class TestConfigurationWithIgnoreErrors { + + private long bar; + + public void setBar(long bar) { + this.bar = bar; + } + + public long getBar() { + return this.bar; + } + } @ConfigurationProperties(prefix = "test") @@ -549,6 +583,7 @@ public class ConfigurationPropertiesBindingPostProcessorTests { } @ConfigurationProperties(prefix = "custom") + @Validated public static class PropertyWithCustomValidator { private String foo; @@ -647,6 +682,7 @@ public class ConfigurationPropertiesBindingPostProcessorTests { @Configuration @EnableConfigurationProperties @ConfigurationProperties(prefix = "test") + @Validated public static class PropertyWithValue { @Value("${default.value}") @@ -669,6 +705,7 @@ public class ConfigurationPropertiesBindingPostProcessorTests { @Configuration @EnableConfigurationProperties + @Validated @ConfigurationProperties(prefix = "test") public static class PropertiesWithMap { diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/EnableConfigurationPropertiesTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/EnableConfigurationPropertiesTests.java index 3f3b16befc..504aec76d3 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/EnableConfigurationPropertiesTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/EnableConfigurationPropertiesTests.java @@ -30,6 +30,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.bind.BindException; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -37,7 +38,6 @@ import org.springframework.context.annotation.ImportResource; import org.springframework.core.env.MutablePropertySources; import org.springframework.stereotype.Component; import org.springframework.test.context.support.TestPropertySourceUtils; -import org.springframework.validation.BindException; import org.springframework.validation.annotation.Validated; import static org.assertj.core.api.Assertions.assertThat; @@ -47,6 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Dave Syer * @author Stephane Nicoll + * @author Madhura Bhave */ public class EnableConfigurationPropertiesTests { @@ -94,30 +95,6 @@ public class EnableConfigurationPropertiesTests { .isEqualTo("bar"); } - @Test - public void testNestedSystemPropertiesBindingWithUnderscore() { - this.context.register(NestedConfiguration.class); - System.setProperty("name", "foo"); - System.setProperty("nested_name", "bar"); - this.context.refresh(); - assertThat(this.context.getBeanNamesForType(NestedProperties.class)).hasSize(1); - assertThat(this.context.getBean(NestedProperties.class).name).isEqualTo("foo"); - assertThat(this.context.getBean(NestedProperties.class).nested.name) - .isEqualTo("bar"); - } - - @Test - public void testNestedOsEnvironmentVariableWithUnderscore() { - TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, - "NAME=foo", "NESTED_NAME=bar"); - this.context.register(NestedConfiguration.class); - this.context.refresh(); - assertThat(this.context.getBeanNamesForType(NestedProperties.class)).hasSize(1); - assertThat(this.context.getBean(NestedProperties.class).name).isEqualTo("foo"); - assertThat(this.context.getBean(NestedProperties.class).nested.name) - .isEqualTo("bar"); - } - @Test public void testStrictPropertiesBinding() { removeSystemProperties(); @@ -134,18 +111,7 @@ public class EnableConfigurationPropertiesTests { public void testPropertiesEmbeddedBinding() { this.context.register(EmbeddedTestConfiguration.class); TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, - "spring_foo_name=foo"); - this.context.refresh(); - assertThat(this.context.getBeanNamesForType(EmbeddedTestProperties.class)) - .hasSize(1); - assertThat(this.context.getBean(TestProperties.class).name).isEqualTo("foo"); - } - - @Test - public void testOsEnvironmentVariableEmbeddedBinding() { - TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, - "SPRING_FOO_NAME=foo"); - this.context.register(EmbeddedTestConfiguration.class); + "spring.foo.name=foo"); this.context.refresh(); assertThat(this.context.getBeanNamesForType(EmbeddedTestProperties.class)) .hasSize(1); @@ -322,15 +288,6 @@ public class EnableConfigurationPropertiesTests { assertThat(this.context.getBean(TestConsumer.class).getName()).isEqualTo("foo"); } - @Test - public void testUnderscoresInPrefix() throws Exception { - TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, - "spring_test_external_val=baz"); - this.context.register(SystemExampleConfig.class); - this.context.refresh(); - assertThat(this.context.getBean(SystemEnvVar.class).getVal()).isEqualTo("baz"); - } - @Test public void testSimpleAutoConfig() throws Exception { TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, @@ -623,7 +580,7 @@ public class EnableConfigurationPropertiesTests { private int[] array; - private final List list = new ArrayList<>(); + private List list = new ArrayList<>(); // No getter - you should be able to bind to a write-only bean @@ -643,6 +600,10 @@ public class EnableConfigurationPropertiesTests { return this.list; } + public void setList(List list) { + this.list = list; + } + } @ConfigurationProperties(ignoreUnknownFields = false) diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java index deeed6601e..76434a558d 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/logback/SpringBootJoranConfiguratorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -28,6 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.impl.StaticLoggerBinder; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.mock.env.MockEnvironment; @@ -139,6 +140,7 @@ public class SpringBootJoranConfiguratorTests { public void relaxedSpringProperty() throws Exception { TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, "my.EXAMPLE_PROPERTY=test"); + ConfigurationPropertySources.attach(this.environment); initialize("property.xml"); assertThat(this.context.getProperty("MINE")).isEqualTo("test"); } diff --git a/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java b/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java index 4f867ee370..7ceaeff8d3 100644 --- a/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -39,7 +39,7 @@ public class SpringProfileDocumentMatcherTests { @Test public void matchesSingleProfile() throws IOException { DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); - Properties properties = getProperties("spring.profiles: foo"); + Properties properties = getProperties("spring.ProfILEs: foo"); assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); }