pull/3741/merge
Phillip Webb 9 years ago
parent db41fb16c0
commit 6193b640a4

@ -6,7 +6,6 @@ something, or simply want to hack on the code this document should help you get
== Using GitHub issues == Using GitHub issues
We use GitHub issues to track bugs and enhancements. If you have a general usage question We use GitHub issues to track bugs and enhancements. If you have a general usage question
please ask on http://stackoverflow.com[Stack Overflow]. The Spring Boot team and the please ask on http://stackoverflow.com[Stack Overflow]. The Spring Boot team and the
broader community monitor the http://stackoverflow.com/tags/spring-boot[`spring-boot`] broader community monitor the http://stackoverflow.com/tags/spring-boot[`spring-boot`]

@ -42,8 +42,8 @@ public class ShellProperties {
private static Log logger = LogFactory.getLog(ShellProperties.class); private static Log logger = LogFactory.getLog(ShellProperties.class);
/** /**
* Authentication type. Auto-detected according to the environment (i.e. if * Authentication type. Auto-detected according to the environment (i.e. if Spring
* Spring Security is available, "spring" is used by default). * Security is available, "spring" is used by default).
*/ */
private String auth = "simple"; private String auth = "simple";

@ -16,14 +16,6 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import static org.hamcrest.Matchers.hasKey;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection; import java.util.Collection;
@ -67,6 +59,14 @@ import org.springframework.util.SocketUtils;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import static org.hamcrest.Matchers.hasKey;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link PublicMetricsAutoConfiguration}. * Tests for {@link PublicMetricsAutoConfiguration}.
* *
@ -246,7 +246,8 @@ public class PublicMetricsAutoConfigurationTests {
} }
context.register(DataSourcePoolMetadataProvidersConfiguration.class, context.register(DataSourcePoolMetadataProvidersConfiguration.class,
CacheStatisticsAutoConfiguration.class, CacheStatisticsAutoConfiguration.class,
PublicMetricsAutoConfiguration.class, MockEmbeddedServletContainerFactory.class); PublicMetricsAutoConfiguration.class,
MockEmbeddedServletContainerFactory.class);
context.refresh(); context.refresh();
this.context = context; this.context = context;
} }

@ -16,9 +16,6 @@
package org.springframework.boot.autoconfigure; package org.springframework.boot.autoconfigure;
import static org.springframework.util.StringUtils.commaDelimitedListToStringArray;
import static org.springframework.util.StringUtils.trimAllWhitespace;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Iterator; import java.util.Iterator;
@ -44,6 +41,9 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static org.springframework.util.StringUtils.commaDelimitedListToStringArray;
import static org.springframework.util.StringUtils.trimAllWhitespace;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for {@link MessageSource}. * {@link EnableAutoConfiguration Auto-configuration} for {@link MessageSource}.
* *
@ -52,7 +52,7 @@ import org.springframework.util.StringUtils;
* @author Eddú Meléndez * @author Eddú Meléndez
*/ */
@Configuration @Configuration
@ConditionalOnMissingBean(value=MessageSource.class, search=SearchStrategy.CURRENT) @ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class) @Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties @EnableConfigurationProperties
@ -81,8 +81,8 @@ public class MessageSourceAutoConfiguration {
/** /**
* Set whether to fall back to the system Locale if no files for a specific Locale * Set whether to fall back to the system Locale if no files for a specific Locale
* have been found. if this is turned off, the only fallback will be the default * have been found. if this is turned off, the only fallback will be the default file
* file (e.g. "messages.properties" for basename "messages"). * (e.g. "messages.properties" for basename "messages").
*/ */
private boolean fallbackToSystemLocale = true; private boolean fallbackToSystemLocale = true;

@ -63,9 +63,8 @@ public @interface SpringBootApplication {
String[] excludeName() default {}; String[] excludeName() default {};
/** /**
* Base packages to scan for annotated components. * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* <p>Use {@link #scanBasePackageClasses} for a type-safe alternative to * for a type-safe alternative to String-based package names.
* String-based package names.
* @return base packages to scan * @return base packages to scan
* @since 1.3.0 * @since 1.3.0
*/ */
@ -73,10 +72,11 @@ public @interface SpringBootApplication {
String[] scanBasePackages() default {}; String[] scanBasePackages() default {};
/** /**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* to scan for annotated components. The package of each class specified will be scanned. * scan for annotated components. The package of each class specified will be scanned.
* <p>Consider creating a special no-op marker class or interface in each package * <p>
* that serves no purpose other than being referenced by this attribute. * Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan * @return base packages to scan
* @since 1.3.0 * @since 1.3.0
*/ */

@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.jdbc;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanClassLoaderAware;
@ -48,8 +49,8 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB
private String name = "testdb"; private String name = "testdb";
/** /**
* Fully qualified name of the connection pool implementation to use. By default, * Fully qualified name of the connection pool implementation to use. By default, it
* it is auto-detected from the classpath. * is auto-detected from the classpath.
*/ */
private Class<? extends DataSource> type; private Class<? extends DataSource> type;

@ -134,8 +134,8 @@ public class JpaProperties {
/** /**
* DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" * DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto"
* property. Default to "create-drop" when using an embedded database, * property. Default to "create-drop" when using an embedded database, "none"
* "none" otherwise. * otherwise.
*/ */
private String ddlAuto; private String ddlAuto;

@ -69,7 +69,6 @@ public class AuthenticationManagerConfiguration {
private static Log logger = LogFactory private static Log logger = LogFactory
.getLog(AuthenticationManagerConfiguration.class); .getLog(AuthenticationManagerConfiguration.class);
@SuppressWarnings("unused")
@Autowired @Autowired
private List<SecurityPrerequisite> dependencies; private List<SecurityPrerequisite> dependencies;

@ -50,8 +50,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
@EnableConfigurationProperties @EnableConfigurationProperties
@Import({ SpringBootWebSecurityConfiguration.class, @Import({ SpringBootWebSecurityConfiguration.class,
AuthenticationManagerConfiguration.class, AuthenticationManagerConfiguration.class,
BootGlobalAuthenticationConfiguration.class, BootGlobalAuthenticationConfiguration.class, SecurityDataConfiguration.class })
SecurityDataConfiguration.class })
public class SecurityAutoConfiguration { public class SecurityAutoConfiguration {
@Bean @Bean

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security; package org.springframework.boot.autoconfigure.security;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.autoconfigure.security; package org.springframework.boot.autoconfigure.security;
import javax.servlet.Filter; import javax.servlet.Filter;
@ -44,15 +45,16 @@ import org.springframework.security.web.context.AbstractSecurityWebApplicationIn
@AutoConfigureAfter(SpringBootWebSecurityConfiguration.class) @AutoConfigureAfter(SpringBootWebSecurityConfiguration.class)
public class SecurityFilterAutoConfiguration { public class SecurityFilterAutoConfiguration {
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
@Bean @Bean
@ConditionalOnBean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) @ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public FilterRegistrationBean securityFilterChainRegistration( public FilterRegistrationBean securityFilterChainRegistration(
@Qualifier(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) Filter securityFilter, @Qualifier(DEFAULT_FILTER_NAME) Filter securityFilter,
SecurityProperties securityProperties) { SecurityProperties securityProperties) {
FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter); FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
registration.setOrder(securityProperties.getFilterOrder()); registration.setOrder(securityProperties.getFilterOrder());
registration registration.setName(DEFAULT_FILTER_NAME);
.setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
return registration; return registration;
} }

@ -40,8 +40,7 @@ public class SecurityProperties implements SecurityPrerequisite {
* useful place to put user-defined access rules if you want to override the default * useful place to put user-defined access rules if you want to override the default
* access rules. * access rules.
*/ */
public static final int ACCESS_OVERRIDE_ORDER = SecurityProperties.BASIC_AUTH_ORDER public static final int ACCESS_OVERRIDE_ORDER = SecurityProperties.BASIC_AUTH_ORDER - 2;
- 2;
/** /**
* Order applied to the WebSecurityConfigurerAdapter that is used to configure basic * Order applied to the WebSecurityConfigurerAdapter that is used to configure basic
@ -62,8 +61,7 @@ public class SecurityProperties implements SecurityPrerequisite {
* other filters registered with the container). There is no connection between this * other filters registered with the container). There is no connection between this
* and the <code>@Order</code> on a WebSecurityConfigurer. * and the <code>@Order</code> on a WebSecurityConfigurer.
*/ */
public static final int DEFAULT_FILTER_ORDER = FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER public static final int DEFAULT_FILTER_ORDER = FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100;
- 100;
/** /**
* Enable secure channel for all requests. * Enable secure channel for all requests.

@ -31,9 +31,10 @@ import org.springframework.context.annotation.Conditional;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.3.0 * @since 1.3.0
*/ */
@Target({ElementType.TYPE, ElementType.METHOD}) @Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
@Conditional(OnEnabledResourceChainCondition.class) @Conditional(OnEnabledResourceChainCondition.class)
public @interface ConditionalOnEnabledResourceChain { public @interface ConditionalOnEnabledResourceChain {
} }

@ -34,14 +34,16 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
class OnEnabledResourceChainCondition extends SpringBootCondition { class OnEnabledResourceChainCondition extends SpringBootCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { public ConditionOutcome getMatchOutcome(ConditionContext context,
ConfigurableEnvironment environment = (ConfigurableEnvironment) context.getEnvironment(); AnnotatedTypeMetadata metadata) {
ResourceProperties resourceProperties = new ResourceProperties(); ConfigurableEnvironment environment = (ConfigurableEnvironment) context
RelaxedDataBinder binder = new RelaxedDataBinder(resourceProperties, "spring.resources"); .getEnvironment();
ResourceProperties properties = new ResourceProperties();
RelaxedDataBinder binder = new RelaxedDataBinder(properties, "spring.resources");
binder.bind(new PropertySourcesPropertyValues(environment.getPropertySources())); binder.bind(new PropertySourcesPropertyValues(environment.getPropertySources()));
Boolean match = properties.getChain().getEnabled();
Boolean match = resourceProperties.getChain().getEnabled(); return new ConditionOutcome(match, "Resource chain is "
return new ConditionOutcome(match, "Resource chain is " + (match ? "enabled" : "disabled" + ")")); + (match ? "enabled" : "disabled" + ")"));
} }
} }

@ -43,8 +43,8 @@ public class MessageSourceAutoConfigurationTests {
@After @After
public void closeContext() { public void closeContext() {
if (context != null) { if (this.context != null) {
context.close(); this.context.close();
} }
} }
@ -72,7 +72,8 @@ public class MessageSourceAutoConfigurationTests {
@Test @Test
public void testMultipleMessageSourceCreated() throws Exception { public void testMultipleMessageSourceCreated() throws Exception {
load("spring.messages.basename:test/messages,test/messages2"); load("spring.messages.basename:test/messages,test/messages2");
assertEquals("bar", this.context.getMessage("foo", null, "Foo message", Locale.UK)); assertEquals("bar",
this.context.getMessage("foo", null, "Foo message", Locale.UK));
assertEquals("bar-bar", assertEquals("bar-bar",
this.context.getMessage("foo-foo", null, "Foo-Foo message", Locale.UK)); this.context.getMessage("foo-foo", null, "Foo-Foo message", Locale.UK));
} }
@ -124,4 +125,5 @@ public class MessageSourceAutoConfigurationTests {
protected static class Config { protected static class Config {
} }
} }

@ -164,7 +164,9 @@ public class JobLauncherCommandLineRunnerTests {
protected static class BatchConfiguration implements BatchConfigurer { protected static class BatchConfiguration implements BatchConfigurer {
private ResourcelessTransactionManager transactionManager = new ResourcelessTransactionManager(); private ResourcelessTransactionManager transactionManager = new ResourcelessTransactionManager();
private JobRepository jobRepository; private JobRepository jobRepository;
private MapJobRepositoryFactoryBean jobRepositoryFactory = new MapJobRepositoryFactoryBean( private MapJobRepositoryFactoryBean jobRepositoryFactory = new MapJobRepositoryFactoryBean(
this.transactionManager); this.transactionManager);
@ -198,6 +200,7 @@ public class JobLauncherCommandLineRunnerTests {
public JobExplorer getJobExplorer() throws Exception { public JobExplorer getJobExplorer() throws Exception {
return new MapJobExplorerFactoryBean(this.jobRepositoryFactory).getObject(); return new MapJobExplorerFactoryBean(this.jobRepositoryFactory).getObject();
} }
} }
} }

@ -26,14 +26,13 @@ import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties; import java.util.Properties;
import java.util.Random; import java.util.Random;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.sql.DataSource; import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSource;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.EnvironmentTestUtils;
@ -43,6 +42,8 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import com.zaxxer.hikari.HikariDataSource;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -158,8 +159,8 @@ public class DataSourceAutoConfigurationTests {
public void explicitType() { public void explicitType() {
EnvironmentTestUtils.addEnvironment(this.context, EnvironmentTestUtils.addEnvironment(this.context,
"spring.datasource.driverClassName:org.hsqldb.jdbcDriver", "spring.datasource.driverClassName:org.hsqldb.jdbcDriver",
"spring.datasource.url:jdbc:hsqldb:mem:testdb", "spring.datasource.url:jdbc:hsqldb:mem:testdb", "spring.datasource.type:"
"spring.datasource.type:" + HikariDataSource.class.getName()); + HikariDataSource.class.getName());
this.context.register(DataSourceAutoConfiguration.class, this.context.register(DataSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class); PropertyPlaceholderAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
@ -271,7 +272,8 @@ public class DataSourceAutoConfigurationTests {
} }
@SuppressWarnings("unused") // see testExplicitDriverClassClearsUserName @SuppressWarnings("unused")
// see testExplicitDriverClassClearsUserName
public static class DatabaseDriver implements Driver { public static class DatabaseDriver implements Driver {
@Override @Override

@ -16,12 +16,6 @@
package org.springframework.boot.autoconfigure.security; package org.springframework.boot.autoconfigure.security;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List; import java.util.List;
import org.junit.After; import org.junit.After;
@ -63,6 +57,12 @@ import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/** /**
* Tests for {@link SecurityAutoConfiguration}. * Tests for {@link SecurityAutoConfiguration}.
* *
@ -105,7 +105,7 @@ public class SecurityAutoConfigurationTests {
PropertyPlaceholderAutoConfiguration.class); PropertyPlaceholderAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
assertEquals( assertEquals(
FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER-100, FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100,
this.context.getBean("securityFilterChainRegistration", this.context.getBean("securityFilterChainRegistration",
FilterRegistrationBean.class).getOrder()); FilterRegistrationBean.class).getOrder());
} }
@ -136,7 +136,7 @@ public class SecurityAutoConfigurationTests {
PropertyPlaceholderAutoConfiguration.class); PropertyPlaceholderAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
assertEquals( assertEquals(
FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER-100, FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100,
this.context.getBean("securityFilterChainRegistration", this.context.getBean("securityFilterChainRegistration",
FilterRegistrationBean.class).getOrder()); FilterRegistrationBean.class).getOrder());
} }
@ -355,11 +355,9 @@ public class SecurityAutoConfigurationTests {
public void testSecurityEvaluationContextExtensionSupport() { public void testSecurityEvaluationContextExtensionSupport() {
this.context = new AnnotationConfigWebApplicationContext(); this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext()); this.context.setServletContext(new MockServletContext());
this.context.register(AuthenticationManagerCustomizer.class, this.context.register(AuthenticationManagerCustomizer.class,
SecurityAutoConfiguration.class, ServerPropertiesAutoConfiguration.class); SecurityAutoConfiguration.class, ServerPropertiesAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
assertNotNull(this.context.getBean(SecurityEvaluationContextExtension.class)); assertNotNull(this.context.getBean(SecurityEvaluationContextExtension.class));
} }

@ -200,11 +200,13 @@ public class ThymeleafAutoConfigurationTests {
this.context.register(ThymeleafAutoConfiguration.class, this.context.register(ThymeleafAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class); PropertyPlaceholderAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
assertEquals(0, this.context.getBeansOfType(ResourceUrlEncodingFilter.class).size()); assertEquals(0, this.context.getBeansOfType(ResourceUrlEncodingFilter.class)
.size());
} }
@Test @Test
public void registerResourceHandlingFilterOnlyIfResourceChainIsEnabled() throws Exception { public void registerResourceHandlingFilterOnlyIfResourceChainIsEnabled()
throws Exception {
this.context.register(ThymeleafAutoConfiguration.class, this.context.register(ThymeleafAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class); PropertyPlaceholderAutoConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.context, EnvironmentTestUtils.addEnvironment(this.context,

@ -193,11 +193,13 @@ public class VelocityAutoConfigurationTests {
@Test @Test
public void registerResourceHandlingFilterDisabledByDefault() throws Exception { public void registerResourceHandlingFilterDisabledByDefault() throws Exception {
registerAndRefreshContext(); registerAndRefreshContext();
assertEquals(0, this.context.getBeansOfType(ResourceUrlEncodingFilter.class).size()); assertEquals(0, this.context.getBeansOfType(ResourceUrlEncodingFilter.class)
.size());
} }
@Test @Test
public void registerResourceHandlingFilterOnlyIfResourceChainIsEnabled() throws Exception { public void registerResourceHandlingFilterOnlyIfResourceChainIsEnabled()
throws Exception {
registerAndRefreshContext("spring.resources.chain.enabled:true"); registerAndRefreshContext("spring.resources.chain.enabled:true");
assertNotNull(this.context.getBean(ResourceUrlEncodingFilter.class)); assertNotNull(this.context.getBean(ResourceUrlEncodingFilter.class));
} }

@ -18,7 +18,6 @@ package org.springframework.boot.autoconfigure.web;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -71,7 +70,6 @@ public class ConditionalOnEnabledResourceChainTests {
assertTrue(this.context.containsBean("foo")); assertTrue(this.context.containsBean("foo"));
} }
private void load(String... environment) { private void load(String... environment) {
this.context.register(Config.class); this.context.register(Config.class);
EnvironmentTestUtils.addEnvironment(this.context, environment); EnvironmentTestUtils.addEnvironment(this.context, environment);

@ -184,7 +184,7 @@ class ProjectGenerationRequest {
* @return the package name or {@code null} * @return the package name or {@code null}
*/ */
public String getPackageName() { public String getPackageName() {
return packageName; return this.packageName;
} }
public void setPackageName(String packageName) { public void setPackageName(String packageName) {

@ -266,9 +266,9 @@ public class InitCommandTests extends AbstractHttpClientMockTests {
public void parseProjectOptions() throws Exception { public void parseProjectOptions() throws Exception {
this.handler.disableProjectGeneration(); this.handler.disableProjectGeneration();
this.command.run("-g=org.demo", "-a=acme", "-v=1.2.3-SNAPSHOT", "-n=acme-sample", this.command.run("-g=org.demo", "-a=acme", "-v=1.2.3-SNAPSHOT", "-n=acme-sample",
"--description=Acme sample project", "--package-name=demo.foo", "-t=ant-project", "--description=Acme sample project", "--package-name=demo.foo",
"--build=grunt", "--format=web", "-p=war", "-j=1.9", "-l=groovy", "-t=ant-project", "--build=grunt", "--format=web", "-p=war", "-j=1.9",
"-b=1.2.0.RELEASE", "-d=web,data-jpa"); "-l=groovy", "-b=1.2.0.RELEASE", "-d=web,data-jpa");
assertEquals("org.demo", this.handler.lastRequest.getGroupId()); assertEquals("org.demo", this.handler.lastRequest.getGroupId());
assertEquals("acme", this.handler.lastRequest.getArtifactId()); assertEquals("acme", this.handler.lastRequest.getArtifactId());
assertEquals("1.2.3-SNAPSHOT", this.handler.lastRequest.getVersion()); assertEquals("1.2.3-SNAPSHOT", this.handler.lastRequest.getVersion());

@ -142,24 +142,21 @@ public class ProjectGenerationRequestTests {
@Test @Test
public void outputCustomizeArtifactId() { public void outputCustomizeArtifactId() {
this.request.setOutput("my-project"); this.request.setOutput("my-project");
assertEquals( assertEquals(createDefaultUrl("?artifactId=my-project&type=test-type"),
createDefaultUrl("?artifactId=my-project&type=test-type"),
this.request.generateUrl(createDefaultMetadata())); this.request.generateUrl(createDefaultMetadata()));
} }
@Test @Test
public void outputArchiveCustomizeArtifactId() { public void outputArchiveCustomizeArtifactId() {
this.request.setOutput("my-project.zip"); this.request.setOutput("my-project.zip");
assertEquals( assertEquals(createDefaultUrl("?artifactId=my-project&type=test-type"),
createDefaultUrl("?artifactId=my-project&type=test-type"),
this.request.generateUrl(createDefaultMetadata())); this.request.generateUrl(createDefaultMetadata()));
} }
@Test @Test
public void outputArchiveWithDotsCustomizeArtifactId() { public void outputArchiveWithDotsCustomizeArtifactId() {
this.request.setOutput("my.nice.project.zip"); this.request.setOutput("my.nice.project.zip");
assertEquals( assertEquals(createDefaultUrl("?artifactId=my.nice.project&type=test-type"),
createDefaultUrl("?artifactId=my.nice.project&type=test-type"),
this.request.generateUrl(createDefaultMetadata())); this.request.generateUrl(createDefaultMetadata()));
} }
@ -167,8 +164,7 @@ public class ProjectGenerationRequestTests {
public void outputDoesNotOverrideCustomArtifactId() { public void outputDoesNotOverrideCustomArtifactId() {
this.request.setOutput("my-project"); this.request.setOutput("my-project");
this.request.setArtifactId("my-id"); this.request.setArtifactId("my-id");
assertEquals( assertEquals(createDefaultUrl("?artifactId=my-id&type=test-type"),
createDefaultUrl("?artifactId=my-id&type=test-type"),
this.request.generateUrl(createDefaultMetadata())); this.request.generateUrl(createDefaultMetadata()));
} }

@ -65,7 +65,6 @@ public class DevToolsProperties {
private static final long DEFAULT_RESTART_QUIET_PERIOD = 400; private static final long DEFAULT_RESTART_QUIET_PERIOD = 400;
/** /**
* Enable automatic restart. * Enable automatic restart.
*/ */
@ -117,7 +116,8 @@ public class DevToolsProperties {
allExclude.addAll(StringUtils.commaDelimitedListToSet(this.exclude)); allExclude.addAll(StringUtils.commaDelimitedListToSet(this.exclude));
} }
if (StringUtils.hasText(this.additionalExclude)) { if (StringUtils.hasText(this.additionalExclude)) {
allExclude.addAll(StringUtils.commaDelimitedListToSet(this.additionalExclude)); allExclude.addAll(StringUtils
.commaDelimitedListToSet(this.additionalExclude));
} }
return allExclude.toArray(new String[allExclude.size()]); return allExclude.toArray(new String[allExclude.size()]);
} }

@ -34,9 +34,11 @@ public class DevToolsPropertiesTests {
public void additionalExcludeKeepsDefaults() { public void additionalExcludeKeepsDefaults() {
DevToolsProperties.Restart restart = this.devToolsProperties.getRestart(); DevToolsProperties.Restart restart = this.devToolsProperties.getRestart();
restart.setAdditionalExclude("foo/**,bar/**"); restart.setAdditionalExclude("foo/**,bar/**");
assertThat(restart.getAllExclude(), arrayContaining("META-INF/maven/**", assertThat(
"META-INF/resources/**", "resources/**", "static/**", "public/**", restart.getAllExclude(),
"templates/**", "foo/**", "bar/**")); arrayContaining("META-INF/maven/**", "META-INF/resources/**",
"resources/**", "static/**", "public/**", "templates/**",
"foo/**", "bar/**"));
} }
@Test @Test

@ -255,4 +255,5 @@ public class LocalDevToolsAutoConfigurationTests {
public static class WebResourcesConfig { public static class WebResourcesConfig {
} }
} }

@ -590,7 +590,8 @@ NOTE: The double backslashes are only required when you're using a properties fi
configuration. If you are using YAML, single backslashes are sufficient and a value configuration. If you are using YAML, single backslashes are sufficient and a value
that's equivalent to the one shown above would be `192\.168\.\d{1,3}\.\d{1,3}`. that's equivalent to the one shown above would be `192\.168\.\d{1,3}\.\d{1,3}`.
NOTE: You can trust all proxies by setting the `internal_proxies` to empty (but don't do this in production). NOTE: You can trust all proxies by setting the `internal_proxies` to empty (but don't do
this in production).
You can take complete control of the configuration of the You can take complete control of the configuration of the
`RemoteIpValve` by switching the automatic one off (i.e. set one of `RemoteIpValve` by switching the automatic one off (i.e. set one of
@ -1164,16 +1165,17 @@ Check out {sc-spring-boot-autoconfigure}/web/WebMvcAutoConfiguration.{sc-ext}[`W
{sc-spring-boot-autoconfigure}/velocity/VelocityAutoConfiguration.{sc-ext}[`VelocityAutoConfiguration`] {sc-spring-boot-autoconfigure}/velocity/VelocityAutoConfiguration.{sc-ext}[`VelocityAutoConfiguration`]
[[howto-customize-view-resolvers-velocity]] [[howto-customize-view-resolvers-velocity]]
=== Velocity === Velocity
By default, Spring Boot configures a `VelocityViewResolver`. If you need a
By default, Spring Boot configures a `VelocityViewResolver`. If you need a `VelocityLayoutViewResolver` `VelocityLayoutViewResolver` instead, you can easily configure your own by creating a bean
instead, you can easily configure your own by creating a bean with name `velocityViewResolver`. You can with name `velocityViewResolver`. You can also inject the `VelocityProperties` instance to
also inject the `VelocityProperties` instance to apply the base defaults to your custom view resolver. apply the base defaults to your custom view resolver.
The following example replaces the auto-configured velocity view resolver with a The following example replaces the auto-configured velocity view resolver with a
`VelocityLayoutViewResolver` defining a customized `layoutUrl` and all settings that would have been `VelocityLayoutViewResolver` defining a customized `layoutUrl` and all settings that would
applied from the auto-configuration: have been applied from the auto-configuration:
[source,java,indent=0,subs="verbatim,quotes,attributes"] [source,java,indent=0,subs="verbatim,quotes,attributes"]
---- ----
@ -1187,6 +1189,7 @@ applied from the auto-configuration:
---- ----
[[howto-logging]] [[howto-logging]]
== Logging == Logging
@ -1456,7 +1459,6 @@ entity manager based on the presence of a bean of that type.
[[howto-use-two-entity-managers]] [[howto-use-two-entity-managers]]
=== Use Two EntityManagers === Use Two EntityManagers
Even if the default `EntityManagerFactory` works fine, you will need to define a new one Even if the default `EntityManagerFactory` works fine, you will need to define a new one
because otherwise the presence of the second bean of that type will switch off the because otherwise the presence of the second bean of that type will switch off the
default. To make it easy to do that you can use the convenient `EntityManagerBuilder` default. To make it easy to do that you can use the convenient `EntityManagerBuilder`
@ -1488,7 +1490,6 @@ Example:
.persistenceUnit("orders") .persistenceUnit("orders")
.build(); .build();
} }
---- ----
The configuration above almost works on its own. To complete the picture you need to The configuration above almost works on its own. To complete the picture you need to
@ -1501,7 +1502,6 @@ If you are using Spring Data, you need to configure `@EnableJpaRepositories` acc
[source,java,indent=0,subs="verbatim,quotes,attributes"] [source,java,indent=0,subs="verbatim,quotes,attributes"]
---- ----
@Configuration @Configuration
@EnableJpaRepositories(basePackageClasses = Customer.class, @EnableJpaRepositories(basePackageClasses = Customer.class,
entityManagerFactoryRef = "customerEntityManagerFactory") entityManagerFactoryRef = "customerEntityManagerFactory")
@ -1515,11 +1515,10 @@ If you are using Spring Data, you need to configure `@EnableJpaRepositories` acc
public class OrderConfiguration { public class OrderConfiguration {
... ...
} }
---- ----
[[howto-use-traditional-persistence-xml]] [[howto-use-traditional-persistence-xml]]
=== Use a traditional persistence.xml === Use a traditional persistence.xml
Spring doesn't require the use of XML to configure the JPA provider, and Spring Boot Spring doesn't require the use of XML to configure the JPA provider, and Spring Boot

@ -1351,9 +1351,10 @@ https://spring.io/blog/2014/07/24/spring-framework-4-1-handling-static-web-resou
and in Spring Framework's {spring-reference}/#mvc-config-static-resources[reference documentation]. and in Spring Framework's {spring-reference}/#mvc-config-static-resources[reference documentation].
==== ====
[[boot-features-spring-mvc-web-binding-initializer]] [[boot-features-spring-mvc-web-binding-initializer]]
==== ConfigurableWebBindingInitializer ==== ConfigurableWebBindingInitializer
Spring MVC uses a `WebBindingInitializer` to initialize a `WebDataBinder` for a particular Spring MVC uses a `WebBindingInitializer` to initialize a `WebDataBinder` for a particular
request. If you create your own `ConfigurableWebBindingInitializer` `@Bean`, Spring Boot request. If you create your own `ConfigurableWebBindingInitializer` `@Bean`, Spring Boot
will automatically configure Spring MVC to use it. will automatically configure Spring MVC to use it.
@ -3419,7 +3420,8 @@ You could also specify the `hazelcast.xml` configuration file to use via configu
Otherwise, Spring Boot tries to find the Hazelcast configuration from the default Otherwise, Spring Boot tries to find the Hazelcast configuration from the default
locations, that is `hazelcast.xml` in the working directory or at the root of the locations, that is `hazelcast.xml` in the working directory or at the root of the
classpath. We also check if the `hazelcast.config` system property is set. Check the classpath. We also check if the `hazelcast.config` system property is set. Check the
http://docs.hazelcast.org/docs/latest/manual/html-single/[Hazelcast documentation] for more details. http://docs.hazelcast.org/docs/latest/manual/html-single/[Hazelcast documentation] for
more details.
NOTE: Spring Boot also has an NOTE: Spring Boot also has an
<<boot-features-caching-provider-hazelcast,explicit caching support for Hazelcast>>. The <<boot-features-caching-provider-hazelcast,explicit caching support for Hazelcast>>. The
@ -3712,6 +3714,7 @@ TIP: A https://github.com/snicoll-demos/spring-boot-master-auto-configuration[de
is available to showcase how you can create a starter step by step. is available to showcase how you can create a starter step by step.
[[boot-features-understanding-auto-configured-beans]] [[boot-features-understanding-auto-configured-beans]]
=== Understanding auto-configured beans === Understanding auto-configured beans
Under the hood, auto-configuration is implemented with standard `@Configuration` classes. Under the hood, auto-configuration is implemented with standard `@Configuration` classes.
@ -3818,9 +3821,9 @@ The `@ConditionalOnExpression` annotation allows configuration to be included ba
result of a {spring-reference}/#expressions[SpEL expression]. result of a {spring-reference}/#expressions[SpEL expression].
[[boot-features-custom-starter]] [[boot-features-custom-starter]]
=== Creating your own starter === Creating your own starter
A full Spring Boot starter for a library may contain the following components: A full Spring Boot starter for a library may contain the following components:
* The `autoconfigure` module that contains the auto-configuration code. * The `autoconfigure` module that contains the auto-configuration code.
@ -3831,10 +3834,11 @@ A full Spring Boot starter for a library may contain the following components:
TIP: You may combine the auto-configuration code and the dependency management in a single TIP: You may combine the auto-configuration code and the dependency management in a single
module if you don't need to separate those two concerns. module if you don't need to separate those two concerns.
[[boot-features-custom-starter-naming]] [[boot-features-custom-starter-naming]]
==== Naming ==== Naming
FPlease make sure to provide a proper namespace for your starter. Do not start your module
Please make sure to provide a proper namespace for your starter. Do not start your module
names with `spring-boot`, even if you are using a different Maven groupId. We may offer an names with `spring-boot`, even if you are using a different Maven groupId. We may offer an
official support for the thing you're auto-configuring in the future. official support for the thing you're auto-configuring in the future.
@ -3854,9 +3858,10 @@ meta-data generation>> so that IDE assistance is available for your keys as well
may want to review the generated meta-data (`META-INF/spring-configuration-metadata.json`) may want to review the generated meta-data (`META-INF/spring-configuration-metadata.json`)
to make sure your keys are properly documented. to make sure your keys are properly documented.
[[boot-features-custom-starter-module-autoconfigure]] [[boot-features-custom-starter-module-autoconfigure]]
==== Autoconfigure module ==== Autoconfigure module
The autoconfigure module contains everything that is necessary to get started with the The autoconfigure module contains everything that is necessary to get started with the
library. It may also contain configuration keys definition (`@ConfigurationProperties`) library. It may also contain configuration keys definition (`@ConfigurationProperties`)
and any callback interface that can be used to further customize how the components are and any callback interface that can be used to further customize how the components are
@ -3866,9 +3871,10 @@ TIP: You should mark the dependencies to the library as optional so that you can
the autoconfigure module in your projects more easily. If you do it that way, the library the autoconfigure module in your projects more easily. If you do it that way, the library
won't be provided and boot will backoff by default. won't be provided and boot will backoff by default.
[[boot-features-custom-starter-module-starter]] [[boot-features-custom-starter-module-starter]]
==== Starter module ==== Starter module
The starter is an empty jar, really. Its only purpose is to provide the necessary The starter is an empty jar, really. Its only purpose is to provide the necessary
dependencies to work with the library; see it as an opinionated view of what is required dependencies to work with the library; see it as an opinionated view of what is required
to get started. to get started.
@ -3879,6 +3885,8 @@ a proper set of _default_ dependencies may be hard if the number of optional dep
is high as you should avoid bringing unnecessary dependencies for a typical usage of the is high as you should avoid bringing unnecessary dependencies for a typical usage of the
library. library.
[[boot-features-websockets]] [[boot-features-websockets]]
== WebSockets == WebSockets
Spring Boot provides WebSockets auto-configuration for embedded Tomcat (8 and 7), Jetty 9 Spring Boot provides WebSockets auto-configuration for embedded Tomcat (8 and 7), Jetty 9

@ -54,7 +54,6 @@ of the library that you want to use.
=== EhCache 2.x === EhCache 2.x
Simply add the `net.sf.ehcache:ehcache` dependency to the project. Since there is a Simply add the `net.sf.ehcache:ehcache` dependency to the project. Since there is a
default `ehcache.xml` configuration file at the root of the classpath, it is automatically default `ehcache.xml` configuration file at the root of the classpath, it is automatically

@ -877,8 +877,8 @@ public class SpringApplication {
public void setApplicationContextClass( public void setApplicationContextClass(
Class<? extends ConfigurableApplicationContext> applicationContextClass) { Class<? extends ConfigurableApplicationContext> applicationContextClass) {
this.applicationContextClass = applicationContextClass; this.applicationContextClass = applicationContextClass;
if (!isSpringWebAvailable() || !WebApplicationContext.class.isAssignableFrom( if (!isSpringWebAvailable()
applicationContextClass)) { || !WebApplicationContext.class.isAssignableFrom(applicationContextClass)) {
this.webEnvironment = false; this.webEnvironment = false;
} }
} }

@ -57,4 +57,12 @@ class OriginCapablePropertyValue extends PropertyValue {
.getName() : "unknown"; .getName() : "unknown";
return "'" + name + "' from '" + source + "'"; return "'" + name + "' from '" + source + "'";
} }
public static PropertyOrigin getOrigin(PropertyValue propertyValue) {
if (propertyValue instanceof OriginCapablePropertyValue) {
return ((OriginCapablePropertyValue) propertyValue).getOrigin();
}
return new OriginCapablePropertyValue(propertyValue).getOrigin();
}
} }

@ -18,7 +18,6 @@ package org.springframework.boot.bind;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -35,6 +34,7 @@ import org.springframework.beans.InvalidPropertyException;
import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.NotWritablePropertyException; import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.PropertyValue; import org.springframework.beans.PropertyValue;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
@ -52,15 +52,11 @@ import org.springframework.validation.DataBinder;
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb * @author Phillip Webb
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Andy Wilkinson
* @see RelaxedNames * @see RelaxedNames
*/ */
public class RelaxedDataBinder extends DataBinder { public class RelaxedDataBinder extends DataBinder {
private static final Set<String> BENIGN_PROPERTY_SOURCE_NAMES = Collections
.unmodifiableSet(new HashSet<String>(Arrays.asList(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)));
private static final Object BLANK = new Object(); private static final Object BLANK = new Object();
private String namePrefix; private String namePrefix;
@ -83,12 +79,12 @@ public class RelaxedDataBinder extends DataBinder {
* @param namePrefix An optional prefix to be used when reading properties * @param namePrefix An optional prefix to be used when reading properties
*/ */
public RelaxedDataBinder(Object target, String namePrefix) { public RelaxedDataBinder(Object target, String namePrefix) {
super(wrapTarget(target), super(wrapTarget(target), (StringUtils.hasLength(namePrefix) ? namePrefix
(StringUtils.hasLength(namePrefix) ? namePrefix : DEFAULT_OBJECT_NAME)); : DEFAULT_OBJECT_NAME));
this.namePrefix = cleanNamePrefix(namePrefix); this.namePrefix = cleanNamePrefix(namePrefix);
} }
private static String cleanNamePrefix(String namePrefix) { private String cleanNamePrefix(String namePrefix) {
if (!StringUtils.hasLength(namePrefix)) { if (!StringUtils.hasLength(namePrefix)) {
return null; return null;
} }
@ -146,8 +142,7 @@ public class RelaxedDataBinder extends DataBinder {
propertyValues = addMapPrefix(propertyValues); propertyValues = addMapPrefix(propertyValues);
} }
BeanWrapper wrapper = new BeanWrapperImpl(target); BeanWrapper wrapper = new BeanWrapperImpl(target);
wrapper.setConversionService( wrapper.setConversionService(new RelaxedConversionService(getConversionService()));
new RelaxedConversionService(getConversionService()));
wrapper.setAutoGrowNestedPaths(true); wrapper.setAutoGrowNestedPaths(true);
List<PropertyValue> sortedValues = new ArrayList<PropertyValue>(); List<PropertyValue> sortedValues = new ArrayList<PropertyValue>();
Set<String> modifiedNames = new HashSet<String>(); Set<String> modifiedNames = new HashSet<String>();
@ -214,9 +209,10 @@ public class RelaxedDataBinder extends DataBinder {
if (name.startsWith(candidate)) { if (name.startsWith(candidate)) {
name = name.substring(candidate.length()); name = name.substring(candidate.length());
if (!(this.ignoreNestedProperties && name.contains("."))) { if (!(this.ignoreNestedProperties && name.contains("."))) {
PropertyOrigin propertyOrigin = findPropertyOrigin(value); PropertyOrigin propertyOrigin = OriginCapablePropertyValue
rtn.addPropertyValue(new OriginCapablePropertyValue(name, .getOrigin(value);
value.getValue(), propertyOrigin)); rtn.addPropertyValue(new OriginCapablePropertyValue(name, value
.getValue(), propertyOrigin));
} }
} }
} }
@ -224,8 +220,7 @@ public class RelaxedDataBinder extends DataBinder {
return rtn; return rtn;
} }
private PropertyValue modifyProperty(BeanWrapper target, private PropertyValue modifyProperty(BeanWrapper target, PropertyValue propertyValue) {
PropertyValue propertyValue) {
String name = propertyValue.getName(); String name = propertyValue.getName();
String normalizedName = normalizePath(target, name); String normalizedName = normalizePath(target, name);
if (!normalizedName.equals(name)) { if (!normalizedName.equals(name)) {
@ -251,55 +246,9 @@ public class RelaxedDataBinder extends DataBinder {
@Override @Override
protected AbstractPropertyBindingResult createBeanPropertyBindingResult() { protected AbstractPropertyBindingResult createBeanPropertyBindingResult() {
return new BeanPropertyBindingResult(getTarget(), getObjectName(), return new RelaxedBeanPropertyBindingResult(getTarget(), getObjectName(),
isAutoGrowNestedPaths(), getAutoGrowCollectionLimit()) { isAutoGrowNestedPaths(), getAutoGrowCollectionLimit(),
@Override getConversionService());
protected BeanWrapper createBeanWrapper() {
BeanWrapper beanWrapper = new BeanWrapperImpl(getTarget()) {
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
try {
super.setPropertyValue(pv);
}
catch (NotWritablePropertyException ex) {
PropertyOrigin origin = findPropertyOrigin(pv);
if (isFatal(origin)) {
if (origin != null) {
throw new RelaxedBindingNotWritablePropertyException(
ex, origin);
}
else {
throw ex;
}
}
else {
logger.debug("Ignoring benign property binding failure",
ex);
}
}
}
};
beanWrapper.setConversionService(
new RelaxedConversionService(getConversionService()));
beanWrapper.registerCustomEditor(InetAddress.class,
new InetAddressEditor());
return beanWrapper;
}
};
}
private boolean isFatal(PropertyOrigin origin) {
if (origin == null) {
return true;
}
return !BENIGN_PROPERTY_SOURCE_NAMES.contains(origin.getSource().getName());
}
private PropertyOrigin findPropertyOrigin(PropertyValue propertyValue) {
if (propertyValue instanceof OriginCapablePropertyValue) {
return ((OriginCapablePropertyValue) propertyValue).getOrigin();
}
return new OriginCapablePropertyValue(propertyValue).getOrigin();
} }
private String initializePath(BeanWrapper wrapper, BeanPath path, int index) { private String initializePath(BeanWrapper wrapper, BeanPath path, int index) {
@ -354,8 +303,8 @@ public class RelaxedDataBinder extends DataBinder {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private boolean isBlanked(BeanWrapper wrapper, String propertyName, String key) { private boolean isBlanked(BeanWrapper wrapper, String propertyName, String key) {
Object value = (wrapper.isReadableProperty(propertyName) Object value = (wrapper.isReadableProperty(propertyName) ? wrapper
? wrapper.getPropertyValue(propertyName) : null); .getPropertyValue(propertyName) : null);
if (value instanceof Map) { if (value instanceof Map) {
if (((Map) value).get(key) == BLANK) { if (((Map) value).get(key) == BLANK) {
return true; return true;
@ -364,8 +313,7 @@ public class RelaxedDataBinder extends DataBinder {
return false; return false;
} }
private void extendCollectionIfNecessary(BeanWrapper wrapper, BeanPath path, private void extendCollectionIfNecessary(BeanWrapper wrapper, BeanPath path, int index) {
int index) {
String name = path.prefix(index); String name = path.prefix(index);
TypeDescriptor elementDescriptor = wrapper.getPropertyTypeDescriptor(name) TypeDescriptor elementDescriptor = wrapper.getPropertyTypeDescriptor(name)
.getElementTypeDescriptor(); .getElementTypeDescriptor();
@ -429,9 +377,8 @@ public class RelaxedDataBinder extends DataBinder {
String nested = resolvePropertyName(target, prefix, candidate.toString()); String nested = resolvePropertyName(target, prefix, candidate.toString());
if (nested != null) { if (nested != null) {
Class<?> type = target.getPropertyType(nested); Class<?> type = target.getPropertyType(nested);
if (type != null && Map.class.isAssignableFrom(type)) { if ((type != null) && Map.class.isAssignableFrom(type)) {
// Special case for map property (gh-3836). Maybe could be fixed // Special case for map property (gh-3836).
// in spring-beans)?
return nested + "[" + name.substring(candidate.length() + 1) + "]"; return nested + "[" + name.substring(candidate.length() + 1) + "]";
} }
String propertyName = resolvePropertyName(target, String propertyName = resolvePropertyName(target,
@ -507,6 +454,9 @@ public class RelaxedDataBinder extends DataBinder {
} }
/**
* A path though properties of a bean.
*/
private static class BeanPath { private static class BeanPath {
private List<PathNode> nodes; private List<PathNode> nodes;
@ -678,4 +628,71 @@ public class RelaxedDataBinder extends DataBinder {
} }
/**
* Extended version of {@link BeanPropertyBindingResult} to support relaxed binding.
*/
private static class RelaxedBeanPropertyBindingResult extends
BeanPropertyBindingResult {
private RelaxedConversionService conversionService;
public RelaxedBeanPropertyBindingResult(Object target, String objectName,
boolean autoGrowNestedPaths, int autoGrowCollectionLimit,
ConversionService conversionService) {
super(target, objectName, autoGrowNestedPaths, autoGrowCollectionLimit);
this.conversionService = new RelaxedConversionService(conversionService);
}
@Override
protected BeanWrapper createBeanWrapper() {
BeanWrapper beanWrapper = new RelaxedBeanWrapper(getTarget());
beanWrapper.setConversionService(this.conversionService);
beanWrapper.registerCustomEditor(InetAddress.class, new InetAddressEditor());
return beanWrapper;
}
}
/**
* Extended version of {@link BeanWrapperImpl} to support relaxed binding.
*/
private static class RelaxedBeanWrapper extends BeanWrapperImpl {
private static final Set<String> BENIGN_PROPERTY_SOURCE_NAMES;
static {
Set<String> names = new HashSet<String>();
names.add(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
names.add(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
BENIGN_PROPERTY_SOURCE_NAMES = Collections.unmodifiableSet(names);
}
public RelaxedBeanWrapper(Object target) {
super(target);
}
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
try {
super.setPropertyValue(pv);
}
catch (NotWritablePropertyException ex) {
PropertyOrigin origin = OriginCapablePropertyValue.getOrigin(pv);
if (isBenign(origin)) {
logger.debug("Ignoring benign property binding failure", ex);
return;
}
if (origin == null) {
throw ex;
}
throw new RelaxedBindingNotWritablePropertyException(ex, origin);
}
}
private boolean isBenign(PropertyOrigin origin) {
String name = (origin == null ? null : origin.getSource().getName());
return BENIGN_PROPERTY_SOURCE_NAMES.contains(name);
}
}
} }

@ -52,7 +52,7 @@ import org.springframework.util.Assert;
public class FilterRegistrationBean extends RegistrationBean { public class FilterRegistrationBean extends RegistrationBean {
/** /**
* Filters that wrap the servlet request should have an order less than or equal to this. * Filters that wrap the servlet request should be ordered less than or equal to this.
*/ */
public static final int REQUEST_WRAPPER_FILTER_MAX_ORDER = 0; public static final int REQUEST_WRAPPER_FILTER_MAX_ORDER = 0;

@ -35,4 +35,5 @@ import org.springframework.beans.factory.annotation.Qualifier;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
public @interface ConfigurationPropertiesBinding { public @interface ConfigurationPropertiesBinding {
} }

@ -72,8 +72,8 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor, public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor,
BeanFactoryAware, ResourceLoaderAware, EnvironmentAware, ApplicationContextAware, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware, ApplicationContextAware,
InitializingBean, DisposableBean, PriorityOrdered { InitializingBean, DisposableBean, PriorityOrdered {
public static final String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator"; public static final String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
@ -105,8 +105,8 @@ InitializingBean, DisposableBean, PriorityOrdered {
private int order = Ordered.HIGHEST_PRECEDENCE + 1; private int order = Ordered.HIGHEST_PRECEDENCE + 1;
/** /**
* A list of custom converters (in addition to the defaults) to use when * A list of custom converters (in addition to the defaults) to use when converting
* converting properties for binding. * properties for binding.
* @param converters the converters to set * @param converters the converters to set
*/ */
@Autowired(required = false) @Autowired(required = false)
@ -262,8 +262,8 @@ InitializingBean, DisposableBean, PriorityOrdered {
@Override @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException { throws BeansException {
ConfigurationProperties annotation = AnnotationUtils ConfigurationProperties annotation = AnnotationUtils.findAnnotation(
.findAnnotation(bean.getClass(), ConfigurationProperties.class); bean.getClass(), ConfigurationProperties.class);
if (annotation != null || bean instanceof ConfigurationPropertiesHolder) { if (annotation != null || bean instanceof ConfigurationPropertiesHolder) {
postProcessBeforeInitialization(bean, beanName, annotation); postProcessBeforeInitialization(bean, beanName, annotation);
} }
@ -283,13 +283,13 @@ InitializingBean, DisposableBean, PriorityOrdered {
private void postProcessBeforeInitialization(Object bean, String beanName, private void postProcessBeforeInitialization(Object bean, String beanName,
ConfigurationProperties annotation) { ConfigurationProperties annotation) {
Object target = (bean instanceof ConfigurationPropertiesHolder Object target = (bean instanceof ConfigurationPropertiesHolder ? ((ConfigurationPropertiesHolder) bean)
? ((ConfigurationPropertiesHolder) bean).getTarget() : bean); .getTarget() : bean);
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>( PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
target); target);
if (annotation != null && annotation.locations().length != 0) { if (annotation != null && annotation.locations().length != 0) {
factory.setPropertySources( factory.setPropertySources(loadPropertySources(annotation.locations(),
loadPropertySources(annotation.locations(), annotation.merge())); annotation.merge()));
} }
else { else {
factory.setPropertySources(this.propertySources); factory.setPropertySources(this.propertySources);
@ -297,15 +297,15 @@ InitializingBean, DisposableBean, PriorityOrdered {
factory.setValidator(determineValidator(bean)); factory.setValidator(determineValidator(bean));
// If no explicit conversion service is provided we add one so that (at least) // If no explicit conversion service is provided we add one so that (at least)
// comma-separated arrays of convertibles can be bound automatically // comma-separated arrays of convertibles can be bound automatically
factory.setConversionService(this.conversionService == null factory.setConversionService(this.conversionService == null ? getDefaultConversionService()
? getDefaultConversionService() : this.conversionService); : this.conversionService);
if (annotation != null) { if (annotation != null) {
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields()); factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields()); factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
factory.setExceptionIfInvalid(annotation.exceptionIfInvalid()); factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties()); factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
String targetName = (StringUtils.hasLength(annotation.value()) String targetName = (StringUtils.hasLength(annotation.value()) ? annotation
? annotation.value() : annotation.prefix()); .value() : annotation.prefix());
if (StringUtils.hasLength(targetName)) { if (StringUtils.hasLength(targetName)) {
factory.setTargetName(targetName); factory.setTargetName(targetName);
} }
@ -316,8 +316,7 @@ InitializingBean, DisposableBean, PriorityOrdered {
catch (Exception ex) { catch (Exception ex) {
String targetClass = ClassUtils.getShortName(target.getClass()); String targetClass = ClassUtils.getShortName(target.getClass());
throw new BeanCreationException(beanName, "Could not bind properties to " throw new BeanCreationException(beanName, "Could not bind properties to "
+ targetClass + " (" + getAnnotationDetails(annotation) + ")", + targetClass + " (" + getAnnotationDetails(annotation) + ")", ex);
ex);
} }
} }
@ -326,18 +325,19 @@ InitializingBean, DisposableBean, PriorityOrdered {
return ""; return "";
} }
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append("prefix=").append((StringUtils.hasLength(annotation.value()) details.append("prefix=").append(
? annotation.value() : annotation.prefix())); (StringUtils.hasLength(annotation.value()) ? annotation.value()
: annotation.prefix()));
details.append(", ignoreInvalidFields=").append(annotation.ignoreInvalidFields()); details.append(", ignoreInvalidFields=").append(annotation.ignoreInvalidFields());
details.append(", ignoreUnknownFields=").append(annotation.ignoreUnknownFields()); details.append(", ignoreUnknownFields=").append(annotation.ignoreUnknownFields());
details.append(", ignoreNestedProperties=") details.append(", ignoreNestedProperties=").append(
.append(annotation.ignoreNestedProperties()); annotation.ignoreNestedProperties());
return details.toString(); return details.toString();
} }
private Validator determineValidator(Object bean) { private Validator determineValidator(Object bean) {
boolean globalValidatorSupportBean = (this.validator != null boolean globalValidatorSupportBean = (this.validator != null && this.validator
&& this.validator.supports(bean.getClass())); .supports(bean.getClass()));
if (ClassUtils.isAssignable(Validator.class, bean.getClass())) { if (ClassUtils.isAssignable(Validator.class, bean.getClass())) {
if (!globalValidatorSupportBean) { if (!globalValidatorSupportBean) {
return (Validator) bean; return (Validator) bean;
@ -352,8 +352,8 @@ InitializingBean, DisposableBean, PriorityOrdered {
try { try {
PropertySourcesLoader loader = new PropertySourcesLoader(); PropertySourcesLoader loader = new PropertySourcesLoader();
for (String location : locations) { for (String location : locations) {
Resource resource = this.resourceLoader Resource resource = this.resourceLoader.getResource(this.environment
.getResource(this.environment.resolvePlaceholders(location)); .resolvePlaceholders(location));
String[] profiles = this.environment.getActiveProfiles(); String[] profiles = this.environment.getActiveProfiles();
for (int i = profiles.length; i-- > 0;) { for (int i = profiles.length; i-- > 0;) {
String profile = profiles[i]; String profile = profiles[i];
@ -377,8 +377,7 @@ InitializingBean, DisposableBean, PriorityOrdered {
private ConversionService getDefaultConversionService() { private ConversionService getDefaultConversionService() {
if (this.defaultConversionService == null) { if (this.defaultConversionService == null) {
DefaultConversionService conversionService = new DefaultConversionService(); DefaultConversionService conversionService = new DefaultConversionService();
this.applicationContext.getAutowireCapableBeanFactory() this.applicationContext.getAutowireCapableBeanFactory().autowireBean(this);
.autowireBean(this);
for (Converter<?, ?> converter : this.converters) { for (Converter<?, ?> converter : this.converters) {
conversionService.addConverter(converter); conversionService.addConverter(converter);
} }
@ -388,8 +387,8 @@ InitializingBean, DisposableBean, PriorityOrdered {
} }
/** /**
* Factory to create JSR 303 LocalValidatorFactoryBean. Inner class to prevent * Factory to create JSR 303 LocalValidatorFactoryBean. Inner class to prevent class
* class loader issues. * loader issues.
*/ */
private static class Jsr303ValidatorFactory { private static class Jsr303ValidatorFactory {
@ -403,8 +402,8 @@ InitializingBean, DisposableBean, PriorityOrdered {
} }
/** /**
* {@link Validator} implementation that wraps {@link Validator} instances and * {@link Validator} implementation that wraps {@link Validator} instances and chains
* chains their execution. * their execution.
*/ */
private static class ChainingValidator implements Validator { private static class ChainingValidator implements Validator {
@ -438,8 +437,7 @@ InitializingBean, DisposableBean, PriorityOrdered {
/** /**
* Convenience class to flatten out a tree of property sources without losing the * 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 * reference to the backing data (which can therefore be updated in the background).
* background).
*/ */
private static class FlatPropertySources implements PropertySources { private static class FlatPropertySources implements PropertySources {

@ -26,8 +26,8 @@ import org.springframework.web.filter.CharacterEncodingFilter;
* @author Phillip Webb * @author Phillip Webb
* @since 1.2.1 * @since 1.2.1
*/ */
public class OrderedCharacterEncodingFilter extends CharacterEncodingFilter public class OrderedCharacterEncodingFilter extends CharacterEncodingFilter implements
implements Ordered { Ordered {
private int order = FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER - 9800; private int order = FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER - 9800;

@ -27,7 +27,7 @@ import org.springframework.web.filter.HiddenHttpMethodFilter;
* @since 1.2.4 * @since 1.2.4
*/ */
public class OrderedHiddenHttpMethodFilter extends HiddenHttpMethodFilter implements public class OrderedHiddenHttpMethodFilter extends HiddenHttpMethodFilter implements
Ordered { Ordered {
/** /**
* The default order is high to ensure the filter is applied before Spring Security. * The default order is high to ensure the filter is applied before Spring Security.

@ -27,7 +27,7 @@ import org.springframework.web.filter.HttpPutFormContentFilter;
* @since 1.3.0 * @since 1.3.0
*/ */
public class OrderedHttpPutFormContentFilter extends HttpPutFormContentFilter implements public class OrderedHttpPutFormContentFilter extends HttpPutFormContentFilter implements
Ordered { Ordered {
/** /**
* Higher order to ensure the filter is applied before Spring Security. * Higher order to ensure the filter is applied before Spring Security.

@ -31,7 +31,6 @@ public interface EnvironmentPostProcessor {
/** /**
* Post-process the given {@code environment} * Post-process the given {@code environment}
*
* @param environment the environment to post-process * @param environment the environment to post-process
* @param application the application to which the environment belongs * @param application the application to which the environment belongs
*/ */

@ -200,7 +200,8 @@ public class LoggingApplicationListener implements GenericApplicationListener {
} }
private String getExceptionConversionWord(ConfigurableEnvironment environment) { private String getExceptionConversionWord(ConfigurableEnvironment environment) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, "logging."); RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment,
"logging.");
return resolver.getProperty("exception-conversion-word", "%rEx"); return resolver.getProperty("exception-conversion-word", "%rEx");
} }

@ -26,6 +26,7 @@ import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter; import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.Assert;
/** /**
* Abstract base class for handlers of Servlet components discovered via classpath * Abstract base class for handlers of Servlet components discovered via classpath
@ -49,15 +50,14 @@ abstract class ServletComponentHandler {
} }
protected String[] extractUrlPatterns(String attribute, Map<String, Object> attributes) { protected String[] extractUrlPatterns(String attribute, Map<String, Object> attributes) {
String[] value = (String[]) attributes.get("value");
String[] urlPatterns = (String[]) attributes.get("urlPatterns"); String[] urlPatterns = (String[]) attributes.get("urlPatterns");
if (urlPatterns.length > 0) { if (urlPatterns.length > 0) {
if (((String[]) attributes.get("value")).length > 0) { Assert.state(value.length == 0, "The urlPatterns and value attributes "
throw new IllegalStateException("The urlPatterns and value attributes "
+ "are mututally exclusive"); + "are mututally exclusive");
}
return urlPatterns; return urlPatterns;
} }
return (String[]) attributes.get("value"); return value;
} }
protected final Map<String, String> extractInitParameters( protected final Map<String, String> extractInitParameters(

@ -16,7 +16,7 @@
package org.springframework.boot.web.servlet; package org.springframework.boot.web.servlet;
import java.util.Arrays; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -43,8 +43,14 @@ import org.springframework.context.annotation.ScannedGenericBeanDefinition;
class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor,
ApplicationContextAware { ApplicationContextAware {
private final List<ServletComponentHandler> handlers = Arrays.asList( private static final List<ServletComponentHandler> HANDLERS;
new WebServletHandler(), new WebFilterHandler(), new WebListenerHandler()); static {
List<ServletComponentHandler> handers = new ArrayList<ServletComponentHandler>();
handers.add(new WebServletHandler());
handers.add(new WebFilterHandler());
handers.add(new WebListenerHandler());
HANDLERS = Collections.unmodifiableList(handers);
}
private final Set<String> packagesToScan; private final Set<String> packagesToScan;
@ -60,18 +66,24 @@ class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcess
if (isRunningInEmbeddedContainer()) { if (isRunningInEmbeddedContainer()) {
ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider(); ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();
for (String packageToScan : this.packagesToScan) { for (String packageToScan : this.packagesToScan) {
scanPackage(componentProvider, packageToScan);
}
}
}
private void scanPackage(
ClassPathScanningCandidateComponentProvider componentProvider,
String packageToScan) {
for (BeanDefinition candidate : componentProvider for (BeanDefinition candidate : componentProvider
.findCandidateComponents(packageToScan)) { .findCandidateComponents(packageToScan)) {
if (candidate instanceof ScannedGenericBeanDefinition) { if (candidate instanceof ScannedGenericBeanDefinition) {
for (ServletComponentHandler handler : this.handlers) { for (ServletComponentHandler handler : HANDLERS) {
handler.handle(((ScannedGenericBeanDefinition) candidate), handler.handle(((ScannedGenericBeanDefinition) candidate),
(BeanDefinitionRegistry) this.applicationContext); (BeanDefinitionRegistry) this.applicationContext);
} }
} }
} }
} }
}
}
private boolean isRunningInEmbeddedContainer() { private boolean isRunningInEmbeddedContainer() {
return this.applicationContext instanceof EmbeddedWebApplicationContext return this.applicationContext instanceof EmbeddedWebApplicationContext
@ -82,7 +94,7 @@ class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcess
private ClassPathScanningCandidateComponentProvider createComponentProvider() { private ClassPathScanningCandidateComponentProvider createComponentProvider() {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider( ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false); false);
for (ServletComponentHandler handler : this.handlers) { for (ServletComponentHandler handler : HANDLERS) {
componentProvider.addIncludeFilter(handler.getTypeFilter()); componentProvider.addIncludeFilter(handler.getTypeFilter());
} }
return componentProvider; return componentProvider;

@ -53,7 +53,6 @@ public @interface ServletComponentScan {
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {@code @ServletComponentScan("org.my.pkg")} instead of * declarations e.g.: {@code @ServletComponentScan("org.my.pkg")} instead of
* {@code @ServletComponentScan(basePackages="org.my.pkg")}. * {@code @ServletComponentScan(basePackages="org.my.pkg")}.
*
* @return the base packages to scan * @return the base packages to scan
*/ */
String[] value() default {}; String[] value() default {};
@ -64,7 +63,6 @@ public @interface ServletComponentScan {
* <p> * <p>
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names. * package names.
*
* @return the base packages to scan * @return the base packages to scan
*/ */
String[] basePackages() default {}; String[] basePackages() default {};
@ -73,8 +71,8 @@ public @interface ServletComponentScan {
* Type-safe alternative to {@link #basePackages()} for specifying the packages to * Type-safe alternative to {@link #basePackages()} for specifying the packages to
* scan for annotated servlet components. The package of each class specified will be * scan for annotated servlet components. The package of each class specified will be
* scanned. * scanned.
*
* @return classes from the base packages to scan * @return classes from the base packages to scan
*/ */
Class<?>[] basePackageClasses() default {}; Class<?>[] basePackageClasses() default {};
} }

@ -53,16 +53,6 @@ class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
} }
} }
private void addPostProcessor(BeanDefinitionRegistry registry,
Set<String> packagesToScan) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(
packagesToScan);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
}
private void updatePostProcessor(BeanDefinitionRegistry registry, private void updatePostProcessor(BeanDefinitionRegistry registry,
Set<String> packagesToScan) { Set<String> packagesToScan) {
BeanDefinition definition = registry.getBeanDefinition(BEAN_NAME); BeanDefinition definition = registry.getBeanDefinition(BEAN_NAME);
@ -75,6 +65,16 @@ class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
constructorArguments.setValue(packagesToScan); constructorArguments.setValue(packagesToScan);
} }
private void addPostProcessor(BeanDefinitionRegistry registry,
Set<String> packagesToScan) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(
packagesToScan);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
}
private Set<String> getPackagesToScan(AnnotationMetadata metadata) { private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata
.getAnnotationAttributes(ServletComponentScan.class.getName())); .getAnnotationAttributes(ServletComponentScan.class.getName()));

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,9 +16,6 @@
package org.springframework.boot.bind; package org.springframework.boot.bind;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -35,6 +32,9 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
/** /**
* Tests for {@link ConfigurationProperties} binding with custom converters. * Tests for {@link ConfigurationProperties} binding with custom converters.
* *
@ -82,6 +82,7 @@ public class ConverterBindingTests {
} }
public static class Foo { public static class Foo {
private String name; private String name;
public String getName() { public String getName() {
@ -96,6 +97,7 @@ public class ConverterBindingTests {
@ConfigurationProperties @ConfigurationProperties
public static class Wrapper { public static class Wrapper {
private Foo foo; private Foo foo;
public Foo getFoo() { public Foo getFoo() {
@ -105,6 +107,7 @@ public class ConverterBindingTests {
public void setFoo(Foo foo) { public void setFoo(Foo foo) {
this.foo = foo; this.foo = foo;
} }
} }
} }

@ -16,15 +16,6 @@
package org.springframework.boot.bind; package org.springframework.boot.bind;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -60,6 +51,15 @@ import org.springframework.validation.DataBinder;
import org.springframework.validation.FieldError; import org.springframework.validation.FieldError;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
/** /**
* Tests for {@link RelaxedDataBinder}. * Tests for {@link RelaxedDataBinder}.
* *

@ -97,7 +97,8 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
fail("Expected exception"); fail("Expected exception");
} }
catch (BeanCreationException ex) { catch (BeanCreationException ex) {
RelaxedBindingNotWritablePropertyException bex = (RelaxedBindingNotWritablePropertyException) ex.getRootCause(); RelaxedBindingNotWritablePropertyException bex = (RelaxedBindingNotWritablePropertyException) ex
.getRootCause();
assertThat(bex.getMessage(), assertThat(bex.getMessage(),
startsWith("Failed to bind 'com.example.baz' from 'test' to 'baz' " startsWith("Failed to bind 'com.example.baz' from 'test' to 'baz' "
+ "property on '" + TestConfiguration.class.getName())); + "property on '" + TestConfiguration.class.getName()));

@ -65,6 +65,7 @@ public class ServletComponentScanIntegrationTests {
public TomcatEmbeddedServletContainerFactory servletContainerFactory() { public TomcatEmbeddedServletContainerFactory servletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory(0); return new TomcatEmbeddedServletContainerFactory(0);
} }
} }
} }

Loading…
Cancel
Save