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