Commit 5502fa29 authored by Phillip Webb's avatar Phillip Webb

Merge branch '1.5.x'

parents 58740d7f 1d55f5f6
...@@ -244,7 +244,8 @@ public class HealthIndicatorAutoConfiguration { ...@@ -244,7 +244,8 @@ public class HealthIndicatorAutoConfiguration {
private final Map<String, LdapOperations> ldapOperations; private final Map<String, LdapOperations> ldapOperations;
public LdapHealthIndicatorConfiguration(Map<String, LdapOperations> ldapOperations) { public LdapHealthIndicatorConfiguration(
Map<String, LdapOperations> ldapOperations) {
this.ldapOperations = ldapOperations; this.ldapOperations = ldapOperations;
} }
......
...@@ -26,7 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -26,7 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Christian Dupuis * @author Christian Dupuis
* @since 1.2.0 * @since 1.2.0
*/ */
@ConfigurationProperties("management.health.status") @ConfigurationProperties(prefix = "management.health.status")
public class HealthIndicatorProperties { public class HealthIndicatorProperties {
/** /**
......
...@@ -20,8 +20,8 @@ import java.net.InetAddress; ...@@ -20,8 +20,8 @@ import java.net.InetAddress;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotNull;
import org.springframework.boot.autoconfigure.security.SecurityPrerequisite; import org.springframework.boot.autoconfigure.security.SecurityPrerequisite;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
...@@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.web.ServerProperties; ...@@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.embedded.Ssl; import org.springframework.boot.context.embedded.Ssl;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
...@@ -78,7 +79,6 @@ public class ManagementServerProperties implements SecurityPrerequisite { ...@@ -78,7 +79,6 @@ public class ManagementServerProperties implements SecurityPrerequisite {
/** /**
* Management endpoint context-path. * Management endpoint context-path.
*/ */
@NotNull
private String contextPath = ""; private String contextPath = "";
/** /**
...@@ -88,6 +88,11 @@ public class ManagementServerProperties implements SecurityPrerequisite { ...@@ -88,6 +88,11 @@ public class ManagementServerProperties implements SecurityPrerequisite {
private final Security security = new Security(); private final Security security = new Security();
@PostConstruct
private void validate() {
Assert.notNull(this.contextPath, "ContextPath must not be null");
}
/** /**
* Returns the management port or {@code null} if the * Returns the management port or {@code null} if the
* {@link ServerProperties#getPort() server port} should be used. * {@link ServerProperties#getPort() server port} should be used.
......
...@@ -29,7 +29,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -29,7 +29,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Phillip Webb * @author Phillip Webb
* @since 1.4.0 * @since 1.4.0
*/ */
@ConfigurationProperties("endpoints.metrics.filter") @ConfigurationProperties(prefix = "endpoints.metrics.filter")
public class MetricFilterProperties { public class MetricFilterProperties {
/** /**
......
...@@ -16,11 +16,13 @@ ...@@ -16,11 +16,13 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import javax.validation.constraints.NotNull; import java.util.regex.Pattern;
import javax.validation.constraints.Pattern;
import javax.annotation.PostConstruct;
import org.springframework.context.EnvironmentAware; import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
/** /**
* Abstract base for {@link Endpoint} implementations. * Abstract base for {@link Endpoint} implementations.
...@@ -31,14 +33,14 @@ import org.springframework.core.env.Environment; ...@@ -31,14 +33,14 @@ import org.springframework.core.env.Environment;
*/ */
public abstract class AbstractEndpoint<T> implements Endpoint<T>, EnvironmentAware { public abstract class AbstractEndpoint<T> implements Endpoint<T>, EnvironmentAware {
private static final Pattern ID_PATTERN = Pattern.compile("\\w+");
private Environment environment; private Environment environment;
/** /**
* Endpoint identifier. With HTTP monitoring the identifier of the endpoint is mapped * Endpoint identifier. With HTTP monitoring the identifier of the endpoint is mapped
* to a URL (e.g. 'foo' is mapped to '/foo'). * to a URL (e.g. 'foo' is mapped to '/foo').
*/ */
@NotNull
@Pattern(regexp = "\\w+", message = "ID must only contains letters, numbers and '_'")
private String id; private String id;
private final boolean sensitiveDefault; private final boolean sensitiveDefault;
...@@ -53,6 +55,13 @@ public abstract class AbstractEndpoint<T> implements Endpoint<T>, EnvironmentAwa ...@@ -53,6 +55,13 @@ public abstract class AbstractEndpoint<T> implements Endpoint<T>, EnvironmentAwa
*/ */
private Boolean enabled; private Boolean enabled;
@PostConstruct
private void validate() {
Assert.notNull(this.id, "Id must not be null");
Assert.isTrue(ID_PATTERN.matcher(this.id).matches(),
"ID must only contains letters, numbers and '_'");
}
/** /**
* Create a new sensitive endpoint instance. The endpoint will enabled flag will be * Create a new sensitive endpoint instance. The endpoint will enabled flag will be
* based on the spring {@link Environment} unless explicitly set. * based on the spring {@link Environment} unless explicitly set.
......
...@@ -62,9 +62,11 @@ public class LiquibaseEndpoint extends AbstractEndpoint<List<LiquibaseReport>> { ...@@ -62,9 +62,11 @@ public class LiquibaseEndpoint extends AbstractEndpoint<List<LiquibaseReport>> {
for (Map.Entry<String, SpringLiquibase> entry : this.liquibases.entrySet()) { for (Map.Entry<String, SpringLiquibase> entry : this.liquibases.entrySet()) {
try { try {
DataSource dataSource = entry.getValue().getDataSource(); DataSource dataSource = entry.getValue().getDataSource();
JdbcConnection connection = new JdbcConnection(dataSource.getConnection()); JdbcConnection connection = new JdbcConnection(
dataSource.getConnection());
try { try {
Database database = factory.findCorrectDatabaseImplementation(connection); Database database = factory
.findCorrectDatabaseImplementation(connection);
reports.add(new LiquibaseReport(entry.getKey(), reports.add(new LiquibaseReport(entry.getKey(),
service.queryDatabaseChangeLogTable(database))); service.queryDatabaseChangeLogTable(database)));
} }
......
...@@ -16,13 +16,13 @@ ...@@ -16,13 +16,13 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import javax.validation.constraints.NotNull; import javax.annotation.PostConstruct;
import javax.validation.constraints.Pattern;
import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.EndpointProperties; import org.springframework.boot.actuate.endpoint.EndpointProperties;
import org.springframework.context.EnvironmentAware; import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/** /**
...@@ -41,8 +41,6 @@ public abstract class AbstractMvcEndpoint extends WebMvcConfigurerAdapter ...@@ -41,8 +41,6 @@ public abstract class AbstractMvcEndpoint extends WebMvcConfigurerAdapter
/** /**
* Endpoint URL path. * Endpoint URL path.
*/ */
@NotNull
@Pattern(regexp = "/.*|^$", message = "Path must start with / or be empty")
private String path; private String path;
/** /**
...@@ -68,6 +66,13 @@ public abstract class AbstractMvcEndpoint extends WebMvcConfigurerAdapter ...@@ -68,6 +66,13 @@ public abstract class AbstractMvcEndpoint extends WebMvcConfigurerAdapter
this.enabled = enabled; this.enabled = enabled;
} }
@PostConstruct
private void validate() {
Assert.notNull(this.path, "Path must not be null");
Assert.isTrue(this.path.length() == 0 || this.path.startsWith("/"),
"Path must start with / or be empty");
}
@Override @Override
public void setEnvironment(Environment environment) { public void setEnvironment(Environment environment) {
this.environment = environment; this.environment = environment;
......
...@@ -27,7 +27,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry ...@@ -27,7 +27,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
* @author Dave Syer * @author Dave Syer
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties("endpoints.docs") @ConfigurationProperties(prefix = "endpoints.docs")
public class DocsMvcEndpoint extends AbstractNamedMvcEndpoint { public class DocsMvcEndpoint extends AbstractNamedMvcEndpoint {
private static final String DOCS_LOCATION = "classpath:/META-INF/resources/spring-boot-actuator/docs/"; private static final String DOCS_LOCATION = "classpath:/META-INF/resources/spring-boot-actuator/docs/";
......
...@@ -31,7 +31,7 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -31,7 +31,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Andy Wilkinson * @author Andy Wilkinson
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties("endpoints.actuator") @ConfigurationProperties(prefix = "endpoints.actuator")
public class HalJsonMvcEndpoint extends AbstractNamedMvcEndpoint { public class HalJsonMvcEndpoint extends AbstractNamedMvcEndpoint {
private final ManagementServletContext managementServletContext; private final ManagementServletContext managementServletContext;
......
...@@ -53,7 +53,7 @@ import org.springframework.web.bind.annotation.ResponseStatus; ...@@ -53,7 +53,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
* @author Phillip Webb * @author Phillip Webb
* @since 1.4.0 * @since 1.4.0
*/ */
@ConfigurationProperties("endpoints.heapdump") @ConfigurationProperties(prefix = "endpoints.heapdump")
@HypermediaDisabled @HypermediaDisabled
public class HeapdumpMvcEndpoint extends AbstractNamedMvcEndpoint { public class HeapdumpMvcEndpoint extends AbstractNamedMvcEndpoint {
......
...@@ -27,7 +27,7 @@ import org.springframework.util.Assert; ...@@ -27,7 +27,7 @@ import org.springframework.util.Assert;
* @author Andy Wilkinson * @author Andy Wilkinson
* @since 1.2.0 * @since 1.2.0
*/ */
@ConfigurationProperties("management.health.diskspace") @ConfigurationProperties(prefix = "management.health.diskspace")
public class DiskSpaceHealthIndicatorProperties { public class DiskSpaceHealthIndicatorProperties {
private static final int MEGABYTES = 1024 * 1024; private static final int MEGABYTES = 1024 * 1024;
......
...@@ -57,6 +57,7 @@ public class LdapHealthIndicator extends AbstractHealthIndicator { ...@@ -57,6 +57,7 @@ public class LdapHealthIndicator extends AbstractHealthIndicator {
} }
return null; return null;
} }
} }
} }
...@@ -32,7 +32,7 @@ import org.springframework.util.PatternMatchUtils; ...@@ -32,7 +32,7 @@ import org.springframework.util.PatternMatchUtils;
* @author Simon Buettner * @author Simon Buettner
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties("spring.metrics.export") @ConfigurationProperties(prefix = "spring.metrics.export")
public class MetricExportProperties extends TriggerProperties { public class MetricExportProperties extends TriggerProperties {
/** /**
......
...@@ -541,8 +541,8 @@ public class HealthIndicatorAutoConfigurationTests { ...@@ -541,8 +541,8 @@ public class HealthIndicatorAutoConfigurationTests {
public void ldapHealthIndicator() throws Exception { public void ldapHealthIndicator() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context, EnvironmentTestUtils.addEnvironment(this.context,
"management.health.diskspace.enabled:false"); "management.health.diskspace.enabled:false");
this.context.register(LdapConfiguration.class, this.context.register(LdapConfiguration.class, ManagementServerProperties.class,
ManagementServerProperties.class, HealthIndicatorAutoConfiguration.class); HealthIndicatorAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
Map<String, HealthIndicator> beans = this.context Map<String, HealthIndicator> beans = this.context
.getBeansOfType(HealthIndicator.class); .getBeansOfType(HealthIndicator.class);
...@@ -556,8 +556,8 @@ public class HealthIndicatorAutoConfigurationTests { ...@@ -556,8 +556,8 @@ public class HealthIndicatorAutoConfigurationTests {
EnvironmentTestUtils.addEnvironment(this.context, EnvironmentTestUtils.addEnvironment(this.context,
"management.health.diskspace.enabled:false", "management.health.diskspace.enabled:false",
"management.health.ldap.enabled:false"); "management.health.ldap.enabled:false");
this.context.register(LdapConfiguration.class, this.context.register(LdapConfiguration.class, ManagementServerProperties.class,
ManagementServerProperties.class, HealthIndicatorAutoConfiguration.class); HealthIndicatorAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
Map<String, HealthIndicator> beans = this.context Map<String, HealthIndicator> beans = this.context
.getBeansOfType(HealthIndicator.class); .getBeansOfType(HealthIndicator.class);
......
...@@ -62,31 +62,33 @@ public class LdapHealthIndicatorTests { ...@@ -62,31 +62,33 @@ public class LdapHealthIndicatorTests {
this.context.register(LdapAutoConfiguration.class, this.context.register(LdapAutoConfiguration.class,
LdapDataAutoConfiguration.class, LdapDataAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
EndpointAutoConfiguration.class, EndpointAutoConfiguration.class, HealthIndicatorAutoConfiguration.class);
HealthIndicatorAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
LdapTemplate ldapTemplate = this.context.getBean(LdapTemplate.class); LdapTemplate ldapTemplate = this.context.getBean(LdapTemplate.class);
assertThat(ldapTemplate).isNotNull(); assertThat(ldapTemplate).isNotNull();
LdapHealthIndicator healthIndicator = this.context.getBean( LdapHealthIndicator healthIndicator = this.context
LdapHealthIndicator.class); .getBean(LdapHealthIndicator.class);
assertThat(healthIndicator).isNotNull(); assertThat(healthIndicator).isNotNull();
} }
@Test @Test
@SuppressWarnings("unchecked")
public void ldapIsUp() { public void ldapIsUp() {
LdapTemplate ldapTemplate = mock(LdapTemplate.class); LdapTemplate ldapTemplate = mock(LdapTemplate.class);
given(ldapTemplate.executeReadOnly(any(ContextExecutor.class))).willReturn("3"); given(ldapTemplate.executeReadOnly((ContextExecutor<String>) any()))
.willReturn("3");
LdapHealthIndicator healthIndicator = new LdapHealthIndicator(ldapTemplate); LdapHealthIndicator healthIndicator = new LdapHealthIndicator(ldapTemplate);
Health health = healthIndicator.health(); Health health = healthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.UP); assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails().get("version")).isEqualTo("3"); assertThat(health.getDetails().get("version")).isEqualTo("3");
verify(ldapTemplate).executeReadOnly(any(ContextExecutor.class)); verify(ldapTemplate).executeReadOnly((ContextExecutor<String>) any());
} }
@Test @Test
@SuppressWarnings("unchecked")
public void ldapIsDown() { public void ldapIsDown() {
LdapTemplate ldapTemplate = mock(LdapTemplate.class); LdapTemplate ldapTemplate = mock(LdapTemplate.class);
given(ldapTemplate.executeReadOnly(any(ContextExecutor.class))) given(ldapTemplate.executeReadOnly((ContextExecutor<String>) any()))
.willThrow(new CommunicationException( .willThrow(new CommunicationException(
new javax.naming.CommunicationException("Connection failed"))); new javax.naming.CommunicationException("Connection failed")));
LdapHealthIndicator healthIndicator = new LdapHealthIndicator(ldapTemplate); LdapHealthIndicator healthIndicator = new LdapHealthIndicator(ldapTemplate);
...@@ -94,7 +96,7 @@ public class LdapHealthIndicatorTests { ...@@ -94,7 +96,7 @@ public class LdapHealthIndicatorTests {
assertThat(health.getStatus()).isEqualTo(Status.DOWN); assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat((String) health.getDetails().get("error")) assertThat((String) health.getDetails().get("error"))
.contains("Connection failed"); .contains("Connection failed");
verify(ldapTemplate).executeReadOnly(any(ContextExecutor.class)); verify(ldapTemplate).executeReadOnly((ContextExecutor<String>) any());
} }
} }
...@@ -26,7 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -26,7 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Vedran Pavic * @author Vedran Pavic
* @since 1.2.0 * @since 1.2.0
*/ */
@ConfigurationProperties("spring.batch") @ConfigurationProperties(prefix = "spring.batch")
public class BatchProperties { public class BatchProperties {
private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/springframework/" private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/springframework/"
......
...@@ -28,8 +28,7 @@ import org.springframework.data.ldap.repository.LdapRepository; ...@@ -28,8 +28,7 @@ import org.springframework.data.ldap.repository.LdapRepository;
import org.springframework.data.ldap.repository.support.LdapRepositoryFactoryBean; import org.springframework.data.ldap.repository.support.LdapRepositoryFactoryBean;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data's LDAP * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's LDAP Repositories.
* Repositories.
* *
* @author Eddú Meléndez * @author Eddú Meléndez
* @since 1.5.0 * @since 1.5.0
......
...@@ -27,7 +27,7 @@ import org.springframework.http.MediaType; ...@@ -27,7 +27,7 @@ import org.springframework.http.MediaType;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties("spring.data.rest") @ConfigurationProperties(prefix = "spring.data.rest")
public class RepositoryRestProperties { public class RepositoryRestProperties {
/** /**
......
...@@ -27,7 +27,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -27,7 +27,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.4.0 * @since 1.4.0
*/ */
@ConfigurationProperties("spring.elasticsearch.jest") @ConfigurationProperties(prefix = "spring.elasticsearch.jest")
public class JestProperties { public class JestProperties {
/** /**
......
...@@ -37,10 +37,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -37,10 +37,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class FlywayProperties { public class FlywayProperties {
/** /**
* Locations of migrations scripts. Can contain the special "{vendor}" placeholder * Locations of migrations scripts. Can contain the special "{vendor}" placeholder to
* to use vendor-specific locations. * use vendor-specific locations.
*/ */
private List<String> locations = new ArrayList<String>(Collections.singletonList("db/migration")); private List<String> locations = new ArrayList<String>(
Collections.singletonList("db/migration"));
/** /**
* Check that migration scripts location exists. * Check that migration scripts location exists.
......
...@@ -16,10 +16,10 @@ ...@@ -16,10 +16,10 @@
package org.springframework.boot.autoconfigure.h2; package org.springframework.boot.autoconfigure.h2;
import javax.validation.constraints.NotNull; import javax.annotation.PostConstruct;
import javax.validation.constraints.Pattern;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
/** /**
* Configuration properties for H2's console. * Configuration properties for H2's console.
...@@ -35,8 +35,6 @@ public class H2ConsoleProperties { ...@@ -35,8 +35,6 @@ public class H2ConsoleProperties {
/** /**
* Path at which the console will be available. * Path at which the console will be available.
*/ */
@NotNull
@Pattern(regexp = "/[^?#]*", message = "Path must start with /")
private String path = "/h2-console"; private String path = "/h2-console";
/** /**
...@@ -46,6 +44,13 @@ public class H2ConsoleProperties { ...@@ -46,6 +44,13 @@ public class H2ConsoleProperties {
private final Settings settings = new Settings(); private final Settings settings = new Settings();
@PostConstruct
private void validate() {
Assert.notNull(this.path, "Path must not be null");
Assert.isTrue(this.path.length() == 0 || this.path.startsWith("/"),
"Path must start with / or be empty");
}
public String getPath() { public String getPath() {
return this.path; return this.path;
} }
......
...@@ -26,7 +26,7 @@ import org.springframework.util.Assert; ...@@ -26,7 +26,7 @@ import org.springframework.util.Assert;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties("spring.hazelcast") @ConfigurationProperties(prefix = "spring.hazelcast")
public class HazelcastProperties { public class HazelcastProperties {
/** /**
......
...@@ -28,7 +28,7 @@ import org.springframework.core.io.Resource; ...@@ -28,7 +28,7 @@ import org.springframework.core.io.Resource;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.4.0 * @since 1.4.0
*/ */
@ConfigurationProperties("spring.info") @ConfigurationProperties(prefix = "spring.info")
public class ProjectInfoProperties { public class ProjectInfoProperties {
private final Build build = new Build(); private final Build build = new Build();
......
...@@ -50,7 +50,7 @@ abstract class DataSourceConfiguration { ...@@ -50,7 +50,7 @@ abstract class DataSourceConfiguration {
static class Tomcat extends DataSourceConfiguration { static class Tomcat extends DataSourceConfiguration {
@Bean @Bean
@ConfigurationProperties("spring.datasource.tomcat") @ConfigurationProperties(prefix = "spring.datasource.tomcat")
public org.apache.tomcat.jdbc.pool.DataSource dataSource( public org.apache.tomcat.jdbc.pool.DataSource dataSource(
DataSourceProperties properties) { DataSourceProperties properties) {
org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource( org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(
...@@ -75,7 +75,7 @@ abstract class DataSourceConfiguration { ...@@ -75,7 +75,7 @@ abstract class DataSourceConfiguration {
static class Hikari extends DataSourceConfiguration { static class Hikari extends DataSourceConfiguration {
@Bean @Bean
@ConfigurationProperties("spring.datasource.hikari") @ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariDataSource dataSource(DataSourceProperties properties) { public HikariDataSource dataSource(DataSourceProperties properties) {
return createDataSource(properties, HikariDataSource.class); return createDataSource(properties, HikariDataSource.class);
} }
...@@ -90,7 +90,7 @@ abstract class DataSourceConfiguration { ...@@ -90,7 +90,7 @@ abstract class DataSourceConfiguration {
static class Dbcp2 extends DataSourceConfiguration { static class Dbcp2 extends DataSourceConfiguration {
@Bean @Bean
@ConfigurationProperties("spring.datasource.dbcp2") @ConfigurationProperties(prefix = "spring.datasource.dbcp2")
public org.apache.commons.dbcp2.BasicDataSource dataSource( public org.apache.commons.dbcp2.BasicDataSource dataSource(
DataSourceProperties properties) { DataSourceProperties properties) {
return createDataSource(properties, return createDataSource(properties,
......
...@@ -29,7 +29,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -29,7 +29,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
@ConfigurationProperties("spring.jersey") @ConfigurationProperties(prefix = "spring.jersey")
public class JerseyProperties { public class JerseyProperties {
/** /**
......
...@@ -54,7 +54,7 @@ class ActiveMQConnectionFactoryConfiguration { ...@@ -54,7 +54,7 @@ class ActiveMQConnectionFactoryConfiguration {
@Bean(destroyMethod = "stop") @Bean(destroyMethod = "stop")
@ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "true", matchIfMissing = false) @ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "true", matchIfMissing = false)
@ConfigurationProperties("spring.activemq.pool.configuration") @ConfigurationProperties(prefix = "spring.activemq.pool.configuration")
public PooledConnectionFactory pooledJmsConnectionFactory( public PooledConnectionFactory pooledJmsConnectionFactory(
ActiveMQProperties properties) { ActiveMQProperties properties) {
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory( PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(
......
...@@ -19,11 +19,12 @@ package org.springframework.boot.autoconfigure.liquibase; ...@@ -19,11 +19,12 @@ package org.springframework.boot.autoconfigure.liquibase;
import java.io.File; import java.io.File;
import java.util.Map; import java.util.Map;
import javax.validation.constraints.NotNull; import javax.annotation.PostConstruct;
import liquibase.integration.spring.SpringLiquibase; import liquibase.integration.spring.SpringLiquibase;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
/** /**
* Configuration properties to configure {@link SpringLiquibase}. * Configuration properties to configure {@link SpringLiquibase}.
...@@ -37,7 +38,6 @@ public class LiquibaseProperties { ...@@ -37,7 +38,6 @@ public class LiquibaseProperties {
/** /**
* Change log configuration path. * Change log configuration path.
*/ */
@NotNull
private String changeLog = "classpath:/db/changelog/db.changelog-master.yaml"; private String changeLog = "classpath:/db/changelog/db.changelog-master.yaml";
/** /**
...@@ -96,6 +96,11 @@ public class LiquibaseProperties { ...@@ -96,6 +96,11 @@ public class LiquibaseProperties {
*/ */
private File rollbackFile; private File rollbackFile;
@PostConstruct
private void validate() {
Assert.notNull(this.changeLog, "ChangeLog must not be null");
}
public String getChangeLog() { public String getChangeLog() {
return this.changeLog; return this.changeLog;
} }
......
...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
@ConfigurationProperties("spring.mobile.devicedelegatingviewresolver") @ConfigurationProperties(prefix = "spring.mobile.devicedelegatingviewresolver")
public class DeviceDelegatingViewResolverProperties { public class DeviceDelegatingViewResolverProperties {
/** /**
......
...@@ -48,8 +48,8 @@ public class SecurityProperties implements SecurityPrerequisite { ...@@ -48,8 +48,8 @@ public class SecurityProperties implements SecurityPrerequisite {
/** /**
* Order applied to the WebSecurityConfigurerAdapter that is used to configure basic * Order applied to the WebSecurityConfigurerAdapter that is used to configure basic
* authentication for application endpoints. If you want to add your own * authentication for application endpoints. If you want to add your own
* authentication for all or some of those endpoints the best thing to do is to add your * authentication for all or some of those endpoints the best thing to do is to add
* own WebSecurityConfigurerAdapter with lower order. * your own WebSecurityConfigurerAdapter with lower order.
*/ */
public static final int BASIC_AUTH_ORDER = Ordered.LOWEST_PRECEDENCE - 5; public static final int BASIC_AUTH_ORDER = Ordered.LOWEST_PRECEDENCE - 5;
......
...@@ -27,7 +27,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -27,7 +27,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties("security.oauth2.client") @ConfigurationProperties(prefix = "security.oauth2.client")
public class OAuth2ClientProperties { public class OAuth2ClientProperties {
/** /**
......
...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Dave Syer * @author Dave Syer
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties("security.oauth2.authorization") @ConfigurationProperties(prefix = "security.oauth2.authorization")
public class AuthorizationServerProperties { public class AuthorizationServerProperties {
/** /**
......
...@@ -174,7 +174,7 @@ public class OAuth2AuthorizationServerConfiguration ...@@ -174,7 +174,7 @@ public class OAuth2AuthorizationServerConfiguration
} }
@Bean @Bean
@ConfigurationProperties("security.oauth2.client") @ConfigurationProperties(prefix = "security.oauth2.client")
public BaseClientDetails oauth2ClientDetails() { public BaseClientDetails oauth2ClientDetails() {
BaseClientDetails details = new BaseClientDetails(); BaseClientDetails details = new BaseClientDetails();
if (this.client.getClientId() == null) { if (this.client.getClientId() == null) {
......
...@@ -31,7 +31,7 @@ import org.springframework.security.oauth2.client.token.grant.code.Authorization ...@@ -31,7 +31,7 @@ import org.springframework.security.oauth2.client.token.grant.code.Authorization
class OAuth2ProtectedResourceDetailsConfiguration { class OAuth2ProtectedResourceDetailsConfiguration {
@Bean @Bean
@ConfigurationProperties("security.oauth2.client") @ConfigurationProperties(prefix = "security.oauth2.client")
@Primary @Primary
public AuthorizationCodeResourceDetails oauth2RemoteResource() { public AuthorizationCodeResourceDetails oauth2RemoteResource() {
return new AuthorizationCodeResourceDetails(); return new AuthorizationCodeResourceDetails();
......
...@@ -74,7 +74,7 @@ public class OAuth2RestOperationsConfiguration { ...@@ -74,7 +74,7 @@ public class OAuth2RestOperationsConfiguration {
protected static class SingletonScopedConfiguration { protected static class SingletonScopedConfiguration {
@Bean @Bean
@ConfigurationProperties("security.oauth2.client") @ConfigurationProperties(prefix = "security.oauth2.client")
@Primary @Primary
public ClientCredentialsResourceDetails oauth2RemoteResource() { public ClientCredentialsResourceDetails oauth2RemoteResource() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails(); ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
......
...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Dave Syer * @author Dave Syer
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties("security.oauth2.sso") @ConfigurationProperties(prefix = "security.oauth2.sso")
public class OAuth2SsoProperties { public class OAuth2SsoProperties {
public static final String DEFAULT_LOGIN_PATH = "/login"; public static final String DEFAULT_LOGIN_PATH = "/login";
......
...@@ -66,11 +66,13 @@ public class DefaultUserInfoRestTemplateFactory implements UserInfoRestTemplateF ...@@ -66,11 +66,13 @@ public class DefaultUserInfoRestTemplateFactory implements UserInfoRestTemplateF
this.oauth2ClientContext = oauth2ClientContext.getIfAvailable(); this.oauth2ClientContext = oauth2ClientContext.getIfAvailable();
} }
@Override
public OAuth2RestTemplate getUserInfoRestTemplate() { public OAuth2RestTemplate getUserInfoRestTemplate() {
if (this.oauth2RestTemplate == null) { if (this.oauth2RestTemplate == null) {
this.oauth2RestTemplate = createOAuth2RestTemplate( this.oauth2RestTemplate = createOAuth2RestTemplate(
this.details == null ? DEFAULT_RESOURCE_DETAILS : this.details); this.details == null ? DEFAULT_RESOURCE_DETAILS : this.details);
this.oauth2RestTemplate.getInterceptors().add(new AcceptJsonRequestInterceptor()); this.oauth2RestTemplate.getInterceptors()
.add(new AcceptJsonRequestInterceptor());
AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider(); AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider();
accessTokenProvider.setTokenRequestEnhancer(new AcceptJsonRequestEnhancer()); accessTokenProvider.setTokenRequestEnhancer(new AcceptJsonRequestEnhancer());
this.oauth2RestTemplate.setAccessTokenProvider(accessTokenProvider); this.oauth2RestTemplate.setAccessTokenProvider(accessTokenProvider);
......
...@@ -37,7 +37,7 @@ import org.springframework.validation.Validator; ...@@ -37,7 +37,7 @@ import org.springframework.validation.Validator;
* @author Dave Syer * @author Dave Syer
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties("security.oauth2.resource") @ConfigurationProperties(prefix = "security.oauth2.resource")
public class ResourceServerProperties implements Validator, BeanFactoryAware { public class ResourceServerProperties implements Validator, BeanFactoryAware {
@JsonIgnore @JsonIgnore
......
...@@ -88,7 +88,8 @@ public class ResourceServerTokenServicesConfiguration { ...@@ -88,7 +88,8 @@ public class ResourceServerTokenServicesConfiguration {
ObjectProvider<List<UserInfoRestTemplateCustomizer>> customizers, ObjectProvider<List<UserInfoRestTemplateCustomizer>> customizers,
ObjectProvider<OAuth2ProtectedResourceDetails> details, ObjectProvider<OAuth2ProtectedResourceDetails> details,
ObjectProvider<OAuth2ClientContext> oauth2ClientContext) { ObjectProvider<OAuth2ClientContext> oauth2ClientContext) {
return new DefaultUserInfoRestTemplateFactory(customizers, details, oauth2ClientContext); return new DefaultUserInfoRestTemplateFactory(customizers, details,
oauth2ClientContext);
} }
@Configuration @Configuration
......
...@@ -30,7 +30,7 @@ import org.springframework.session.hazelcast.HazelcastFlushMode; ...@@ -30,7 +30,7 @@ import org.springframework.session.hazelcast.HazelcastFlushMode;
* @author Vedran Pavic * @author Vedran Pavic
* @since 1.4.0 * @since 1.4.0
*/ */
@ConfigurationProperties("spring.session") @ConfigurationProperties(prefix = "spring.session")
public class SessionProperties { public class SessionProperties {
/** /**
......
...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
@ConfigurationProperties("spring.social.facebook") @ConfigurationProperties(prefix = "spring.social.facebook")
public class FacebookProperties extends SocialProperties { public class FacebookProperties extends SocialProperties {
} }
...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
@ConfigurationProperties("spring.social.linkedin") @ConfigurationProperties(prefix = "spring.social.linkedin")
public class LinkedInProperties extends SocialProperties { public class LinkedInProperties extends SocialProperties {
} }
...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
@ConfigurationProperties("spring.social.twitter") @ConfigurationProperties(prefix = "spring.social.twitter")
public class TwitterProperties extends SocialProperties { public class TwitterProperties extends SocialProperties {
} }
...@@ -27,7 +27,7 @@ import org.springframework.util.MimeType; ...@@ -27,7 +27,7 @@ import org.springframework.util.MimeType;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
@ConfigurationProperties("spring.thymeleaf") @ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties { public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8"); private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8");
......
...@@ -27,7 +27,7 @@ import org.springframework.transaction.support.AbstractPlatformTransactionManage ...@@ -27,7 +27,7 @@ import org.springframework.transaction.support.AbstractPlatformTransactionManage
* @author Phillip Webb * @author Phillip Webb
* @since 1.5.0 * @since 1.5.0
*/ */
@ConfigurationProperties("spring.transaction") @ConfigurationProperties(prefix = "spring.transaction")
public class TransactionProperties implements public class TransactionProperties implements
PlatformTransactionManagerCustomizer<AbstractPlatformTransactionManager> { PlatformTransactionManagerCustomizer<AbstractPlatformTransactionManager> {
......
...@@ -71,7 +71,7 @@ class BitronixJtaConfiguration { ...@@ -71,7 +71,7 @@ class BitronixJtaConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
@ConfigurationProperties("spring.jta.bitronix.properties") @ConfigurationProperties(prefix = "spring.jta.bitronix.properties")
public bitronix.tm.Configuration bitronixConfiguration() { public bitronix.tm.Configuration bitronixConfiguration() {
bitronix.tm.Configuration config = TransactionManagerServices.getConfiguration(); bitronix.tm.Configuration config = TransactionManagerServices.getConfiguration();
if (StringUtils.hasText(this.jtaProperties.getTransactionManagerId())) { if (StringUtils.hasText(this.jtaProperties.getTransactionManagerId())) {
......
...@@ -26,11 +26,11 @@ import java.util.List; ...@@ -26,11 +26,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.PostConstruct;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.SessionCookieConfig; import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode; import javax.servlet.SessionTrackingMode;
import javax.validation.constraints.NotNull;
import io.undertow.Undertow.Builder; import io.undertow.Undertow.Builder;
import io.undertow.UndertowOptions; import io.undertow.UndertowOptions;
...@@ -74,6 +74,7 @@ import org.springframework.boot.web.servlet.ServletContextInitializer; ...@@ -74,6 +74,7 @@ import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.EnvironmentAware; import org.springframework.context.EnvironmentAware;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
...@@ -122,7 +123,6 @@ public class ServerProperties ...@@ -122,7 +123,6 @@ public class ServerProperties
/** /**
* Path of the main dispatcher servlet. * Path of the main dispatcher servlet.
*/ */
@NotNull
private String servletPath = "/"; private String servletPath = "/";
/** /**
...@@ -171,6 +171,11 @@ public class ServerProperties ...@@ -171,6 +171,11 @@ public class ServerProperties
private Environment environment; private Environment environment;
@PostConstruct
private void validate() {
Assert.notNull(this.servletPath, "ServletPath must not be null");
}
@Override @Override
public int getOrder() { public int getOrder() {
return 0; return 0;
......
...@@ -33,7 +33,7 @@ import org.springframework.validation.DefaultMessageCodesResolver; ...@@ -33,7 +33,7 @@ import org.springframework.validation.DefaultMessageCodesResolver;
* @author Eddú Meléndez * @author Eddú Meléndez
* @since 1.1 * @since 1.1
*/ */
@ConfigurationProperties("spring.mvc") @ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties { public class WebMvcProperties {
/** /**
......
...@@ -19,10 +19,10 @@ package org.springframework.boot.autoconfigure.webservices; ...@@ -19,10 +19,10 @@ package org.springframework.boot.autoconfigure.webservices;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.validation.constraints.NotNull; import javax.annotation.PostConstruct;
import javax.validation.constraints.Pattern;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
/** /**
* {@link ConfigurationProperties} for Spring Web Services. * {@link ConfigurationProperties} for Spring Web Services.
...@@ -31,18 +31,23 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -31,18 +31,23 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.4.0 * @since 1.4.0
*/ */
@ConfigurationProperties("spring.webservices") @ConfigurationProperties(prefix = "spring.webservices")
public class WebServicesProperties { public class WebServicesProperties {
/** /**
* Path that serves as the base URI for the services. * Path that serves as the base URI for the services.
*/ */
@NotNull
@Pattern(regexp = "/[^?#]*", message = "Path must start with /")
private String path = "/services"; private String path = "/services";
private final Servlet servlet = new Servlet(); private final Servlet servlet = new Servlet();
@PostConstruct
private void validate() {
Assert.notNull(this.path, "Path must not be null");
Assert.isTrue(this.path.length() == 0 || this.path.startsWith("/"),
"Path must start with / or be empty");
}
public String getPath() { public String getPath() {
return this.path; return this.path;
} }
......
...@@ -222,13 +222,16 @@ public class RepositoryRestMvcAutoConfigurationTests { ...@@ -222,13 +222,16 @@ public class RepositoryRestMvcAutoConfigurationTests {
} }
static class TestRepositoryRestConfigurer extends RepositoryRestConfigurerAdapter { static class TestRepositoryRestConfigurer extends RepositoryRestConfigurerAdapter {
@Override @Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { public void configureRepositoryRestConfiguration(
RepositoryRestConfiguration config) {
config.setRepositoryDetectionStrategy(RepositoryDetectionStrategies.ALL); config.setRepositoryDetectionStrategy(RepositoryDetectionStrategies.ALL);
config.setDefaultMediaType(MediaType.parseMediaType( config.setDefaultMediaType(
"application/my-custom-json")); MediaType.parseMediaType("application/my-custom-json"));
config.setMaxPageSize(78); config.setMaxPageSize(78);
} }
} }
} }
...@@ -220,7 +220,7 @@ public class ResourceServerTokenServicesConfigurationTests { ...@@ -220,7 +220,7 @@ public class ResourceServerTokenServicesConfigurationTests {
"security.oauth2.resource.userInfoUri:http://example.com"); "security.oauth2.resource.userInfoUri:http://example.com");
this.context = new SpringApplicationBuilder( this.context = new SpringApplicationBuilder(
CustomUserInfoRestTemplateFactory.class, ResourceConfiguration.class) CustomUserInfoRestTemplateFactory.class, ResourceConfiguration.class)
.environment(this.environment).web(false).run(); .environment(this.environment).web(false).run();
assertThat(this.context.getBeansOfType(UserInfoRestTemplateFactory.class)) assertThat(this.context.getBeansOfType(UserInfoRestTemplateFactory.class))
.hasSize(1); .hasSize(1);
assertThat(this.context.getBean(UserInfoRestTemplateFactory.class)) assertThat(this.context.getBean(UserInfoRestTemplateFactory.class))
...@@ -331,8 +331,8 @@ public class ResourceServerTokenServicesConfigurationTests { ...@@ -331,8 +331,8 @@ public class ResourceServerTokenServicesConfigurationTests {
protected static class CustomUserInfoRestTemplateFactory protected static class CustomUserInfoRestTemplateFactory
implements UserInfoRestTemplateFactory { implements UserInfoRestTemplateFactory {
private final OAuth2RestTemplate restTemplate = private final OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(
new OAuth2RestTemplate(new AuthorizationCodeResourceDetails()); new AuthorizationCodeResourceDetails());
@Override @Override
public OAuth2RestTemplate getUserInfoRestTemplate() { public OAuth2RestTemplate getUserInfoRestTemplate() {
......
...@@ -1090,13 +1090,16 @@ only rely on custom converters qualified with `@ConfigurationPropertiesBinding`. ...@@ -1090,13 +1090,16 @@ only rely on custom converters qualified with `@ConfigurationPropertiesBinding`.
[[boot-features-external-config-validation]] [[boot-features-external-config-validation]]
==== @ConfigurationProperties Validation ==== @ConfigurationProperties Validation
Spring Boot will attempt to validate external configuration, by default using JSR-303 Spring Boot will attempt to validate `@ConfigurationProperties` classes whenever they
(if it is on the classpath). You can simply add JSR-303 `javax.validation` constraint annotated with Spring's `@Validated` annotation. You can use JSR-303 `javax.validation`
annotations to your `@ConfigurationProperties` class: constraint annotations directly on your configuration class. Simply ensure that a
compliant JSR-303 implementation is on your classpath, then add constraint annotations to
your fields:
[source,java,indent=0] [source,java,indent=0]
---- ----
@ConfigurationProperties(prefix="foo") @ConfigurationProperties(prefix="foo")
@Validated
public class FooProperties { public class FooProperties {
@NotNull @NotNull
...@@ -1114,6 +1117,7 @@ as `@Valid` to trigger its validation. For example, building upon the above ...@@ -1114,6 +1117,7 @@ as `@Valid` to trigger its validation. For example, building upon the above
[source,java,indent=0] [source,java,indent=0]
---- ----
@ConfigurationProperties(prefix="connection") @ConfigurationProperties(prefix="connection")
@Validated
public class FooProperties { public class FooProperties {
@NotNull @NotNull
......
...@@ -775,6 +775,12 @@ ...@@ -775,6 +775,12 @@
<name>m2e.version</name> <name>m2e.version</name>
</property> </property>
</activation> </activation>
<properties>
<m2e.jaxrs.activation>false</m2e.jaxrs.activation>
<m2e.jpa.activation>false</m2e.jpa.activation>
<m2e.jsf.activation>false</m2e.jsf.activation>
<m2e.cdi.activation>false</m2e.cdi.activation>
</properties>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
......
...@@ -24,6 +24,10 @@ ...@@ -24,6 +24,10 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<!-- Test --> <!-- Test -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
......
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.simple;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "sample")
public class SampleConfigurationProperties {
@NotNull
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
...@@ -58,6 +58,10 @@ public class SampleSimpleApplicationTests { ...@@ -58,6 +58,10 @@ public class SampleSimpleApplicationTests {
SampleSimpleApplication.main(new String[0]); SampleSimpleApplication.main(new String[0]);
String output = this.outputCapture.toString(); String output = this.outputCapture.toString();
assertThat(output).contains("Hello Phil"); assertThat(output).contains("Hello Phil");
assertThat(output).contains("The @ConfigurationProperties bean class "
+ "sample.simple.SampleConfigurationProperties contains "
+ "validation constraints but had not been annotated "
+ "with @Validated");
} }
@Test @Test
......
...@@ -56,7 +56,7 @@ public class RestDocsAutoConfiguration { ...@@ -56,7 +56,7 @@ public class RestDocsAutoConfiguration {
} }
@Bean @Bean
@ConfigurationProperties("spring.test.restdocs") @ConfigurationProperties(prefix = "spring.test.restdocs")
public RestDocsMockMvcBuilderCustomizer restDocumentationConfigurer( public RestDocsMockMvcBuilderCustomizer restDocumentationConfigurer(
MockMvcRestDocumentationConfigurer configurer, MockMvcRestDocumentationConfigurer configurer,
ObjectProvider<RestDocumentationResultHandler> resultHandler) { ObjectProvider<RestDocumentationResultHandler> resultHandler) {
......
...@@ -74,7 +74,7 @@ public class MockMvcAutoConfiguration { ...@@ -74,7 +74,7 @@ public class MockMvcAutoConfiguration {
} }
@Bean @Bean
@ConfigurationProperties("spring.test.mockmvc") @ConfigurationProperties(prefix = "spring.test.mockmvc")
public SpringBootMockMvcBuilderCustomizer springBootMockMvcBuilderCustomizer() { public SpringBootMockMvcBuilderCustomizer springBootMockMvcBuilderCustomizer() {
return new SpringBootMockMvcBuilderCustomizer(this.context); return new SpringBootMockMvcBuilderCustomizer(this.context);
} }
......
...@@ -267,8 +267,9 @@ public class PropertiesConfigurationFactory<T> ...@@ -267,8 +267,9 @@ public class PropertiesConfigurationFactory<T>
relaxedTargetNames); relaxedTargetNames);
dataBinder.bind(propertyValues); dataBinder.bind(propertyValues);
if (this.validator != null) { if (this.validator != null) {
validate(dataBinder); dataBinder.validate();
} }
checkForBindingErrors(dataBinder);
} }
private Iterable<String> getRelaxedTargetNames() { private Iterable<String> getRelaxedTargetNames() {
...@@ -338,8 +339,8 @@ public class PropertiesConfigurationFactory<T> ...@@ -338,8 +339,8 @@ public class PropertiesConfigurationFactory<T>
return this.target != null && Map.class.isAssignableFrom(this.target.getClass()); return this.target != null && Map.class.isAssignableFrom(this.target.getClass());
} }
private void validate(RelaxedDataBinder dataBinder) throws BindException { private void checkForBindingErrors(RelaxedDataBinder dataBinder)
dataBinder.validate(); throws BindException {
BindingResult errors = dataBinder.getBindingResult(); BindingResult errors = dataBinder.getBindingResult();
if (errors.hasErrors()) { if (errors.hasErrors()) {
logger.error("Properties configuration failed validation"); logger.error("Properties configuration failed validation");
......
...@@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy; ...@@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.AliasFor;
import org.springframework.validation.annotation.Validated;
/** /**
* Annotation for externalized configuration. Add this to a class definition or a * Annotation for externalized configuration. Add this to a class definition or a
...@@ -80,9 +81,10 @@ public @interface ConfigurationProperties { ...@@ -80,9 +81,10 @@ public @interface ConfigurationProperties {
boolean ignoreUnknownFields() default true; boolean ignoreUnknownFields() default true;
/** /**
* Flag to indicate that an exception should be raised if a Validator is available and * Flag to indicate that an exception should be raised if a Validator is available,
* validation fails. If it is set to false, validation errors will be swallowed. They * the class is annotated with {@link Validated @Validated} and validation fails. If
* will be logged, but not propagated to the caller. * it is set to false, validation errors will be swallowed. They will be logged, but
* not propagated to the caller.
* @return the flag value (default true) * @return the flag value (default true)
*/ */
boolean exceptionIfInvalid() default true; boolean exceptionIfInvalid() default true;
......
...@@ -45,6 +45,7 @@ import org.springframework.context.event.ContextRefreshedEvent; ...@@ -45,6 +45,7 @@ import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered; import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
...@@ -61,6 +62,7 @@ import org.springframework.util.ClassUtils; ...@@ -61,6 +62,7 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.validation.Errors; import org.springframework.validation.Errors;
import org.springframework.validation.Validator; import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
/** /**
...@@ -362,8 +364,8 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc ...@@ -362,8 +364,8 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
return this.validator; return this.validator;
} }
if (this.localValidator == null && isJsr303Present()) { if (this.localValidator == null && isJsr303Present()) {
this.localValidator = new LocalValidatorFactory() this.localValidator = new ValidatedLocalValidatorFactoryBean(
.run(this.applicationContext); this.applicationContext);
} }
return this.localValidator; return this.localValidator;
} }
...@@ -394,18 +396,38 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc ...@@ -394,18 +396,38 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
} }
/** /**
* Factory to create JSR 303 LocalValidatorFactoryBean. Inner class to prevent class * {@link LocalValidatorFactoryBean} supports classes annotated with
* loader issues. * {@link Validated @Validated}.
*/ */
private static class LocalValidatorFactory { private static class ValidatedLocalValidatorFactoryBean
extends LocalValidatorFactoryBean {
public Validator run(ApplicationContext applicationContext) {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); private static final Log logger = LogFactory
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory(); .getLog(ConfigurationPropertiesBindingPostProcessor.class);
validator.setApplicationContext(applicationContext);
validator.setMessageInterpolator(interpolatorFactory.getObject()); ValidatedLocalValidatorFactoryBean(ApplicationContext applicationContext) {
validator.afterPropertiesSet(); setApplicationContext(applicationContext);
return validator; setMessageInterpolator(new MessageInterpolatorFactory().getObject());
afterPropertiesSet();
}
@Override
public boolean supports(Class<?> type) {
if (!super.supports(type)) {
return false;
}
if (AnnotatedElementUtils.isAnnotated(type, Validated.class)) {
return true;
}
if (type.getPackage().getName().startsWith("org.springframework.boot")) {
return false;
}
if (getConstraintsForClass(type).isBeanConstrained()) {
logger.warn("The @ConfigurationProperties bean " + type
+ " contains validation constraints but had not been annotated "
+ "with @Validated.");
}
return true;
} }
} }
......
...@@ -33,7 +33,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -33,7 +33,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @since 1.2.0 * @since 1.2.0
* @see #asProperties() * @see #asProperties()
*/ */
@ConfigurationProperties("spring.jta.atomikos.properties") @ConfigurationProperties(prefix = "spring.jta.atomikos.properties")
public class AtomikosProperties { public class AtomikosProperties {
private final Map<String, String> values = new TreeMap<String, String>(); private final Map<String, String> values = new TreeMap<String, String>();
......
...@@ -46,6 +46,7 @@ import org.springframework.validation.BindException; ...@@ -46,6 +46,7 @@ import org.springframework.validation.BindException;
import org.springframework.validation.Errors; import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils; import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator; import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
...@@ -456,6 +457,7 @@ public class ConfigurationPropertiesBindingPostProcessorTests { ...@@ -456,6 +457,7 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
} }
@ConfigurationProperties(prefix = "test") @ConfigurationProperties(prefix = "test")
@Validated
public static class PropertyWithJSR303 extends PropertyWithoutJSR303 { public static class PropertyWithJSR303 extends PropertyWithoutJSR303 {
@NotNull @NotNull
......
...@@ -38,6 +38,7 @@ import org.springframework.core.env.MutablePropertySources; ...@@ -38,6 +38,7 @@ import org.springframework.core.env.MutablePropertySources;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.test.context.support.TestPropertySourceUtils;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.validation.annotation.Validated;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -172,6 +173,17 @@ public class EnableConfigurationPropertiesTests { ...@@ -172,6 +173,17 @@ public class EnableConfigurationPropertiesTests {
this.context.refresh(); this.context.refresh();
} }
@Test
public void testNoExceptionOnValidationWithoutValidated() {
this.context.register(IgnoredIfInvalidButNotValidatedTestConfiguration.class);
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
"name:foo");
this.context.refresh();
IgnoredIfInvalidButNotValidatedTestProperties bean = this.context
.getBean(IgnoredIfInvalidButNotValidatedTestProperties.class);
assertThat(bean.getDescription()).isNull();
}
@Test @Test
public void testNoExceptionOnValidation() { public void testNoExceptionOnValidation() {
this.context.register(NoExceptionIfInvalidTestConfiguration.class); this.context.register(NoExceptionIfInvalidTestConfiguration.class);
...@@ -432,6 +444,12 @@ public class EnableConfigurationPropertiesTests { ...@@ -432,6 +444,12 @@ public class EnableConfigurationPropertiesTests {
} }
@Configuration
@EnableConfigurationProperties(IgnoredIfInvalidButNotValidatedTestProperties.class)
protected static class IgnoredIfInvalidButNotValidatedTestConfiguration {
}
@Configuration @Configuration
@EnableConfigurationProperties(NoExceptionIfInvalidTestProperties.class) @EnableConfigurationProperties(NoExceptionIfInvalidTestProperties.class)
protected static class NoExceptionIfInvalidTestConfiguration { protected static class NoExceptionIfInvalidTestConfiguration {
...@@ -658,6 +676,7 @@ public class EnableConfigurationPropertiesTests { ...@@ -658,6 +676,7 @@ public class EnableConfigurationPropertiesTests {
} }
@ConfigurationProperties @ConfigurationProperties
@Validated
protected static class ExceptionIfInvalidTestProperties extends TestProperties { protected static class ExceptionIfInvalidTestProperties extends TestProperties {
@NotNull @NotNull
...@@ -673,7 +692,25 @@ public class EnableConfigurationPropertiesTests { ...@@ -673,7 +692,25 @@ public class EnableConfigurationPropertiesTests {
} }
@ConfigurationProperties
protected static class IgnoredIfInvalidButNotValidatedTestProperties
extends TestProperties {
@NotNull
private String description;
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
}
@ConfigurationProperties(exceptionIfInvalid = false) @ConfigurationProperties(exceptionIfInvalid = false)
@Validated
protected static class NoExceptionIfInvalidTestProperties extends TestProperties { protected static class NoExceptionIfInvalidTestProperties extends TestProperties {
@NotNull @NotNull
......
...@@ -32,6 +32,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties ...@@ -32,6 +32,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.diagnostics.FailureAnalysis; import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.validation.annotation.Validated;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -90,6 +91,7 @@ public class BindFailureAnalyzerTests { ...@@ -90,6 +91,7 @@ public class BindFailureAnalyzerTests {
} }
@ConfigurationProperties("test.foo") @ConfigurationProperties("test.foo")
@Validated
static class ValidationFailureProperties { static class ValidationFailureProperties {
@NotNull @NotNull
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment