This commit is contained in:
Phillip Webb 2017-07-11 13:55:45 -07:00
parent 03f74e96b0
commit 8e3baf3130
54 changed files with 381 additions and 330 deletions

View File

@ -17,7 +17,6 @@
package org.springframework.boot.actuate.health;
import java.util.Collections;
import java.util.Map;
import org.neo4j.ogm.model.Result;
import org.neo4j.ogm.session.Session;
@ -51,11 +50,8 @@ public class Neo4jHealthIndicator extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
Session session = this.sessionFactory.openSession();
Result result = session.query(CYPHER, Collections.EMPTY_MAP);
Iterable<Map<String, Object>> results = result.queryResults();
int nodes = (int) results.iterator().next().get("nodes");
Result result = session.query(CYPHER, Collections.emptyMap());
int nodes = (int) result.queryResults().iterator().next().get("nodes");
builder.up().withDetail("nodes", nodes);
}

View File

@ -79,9 +79,8 @@ import static org.mockito.Mockito.mock;
*/
public class HealthIndicatorAutoConfigurationTests {
public final ContextLoader contextLoader = ContextLoader.standard()
.autoConfig(HealthIndicatorAutoConfiguration.class,
ManagementServerProperties.class);
public final ContextLoader contextLoader = ContextLoader.standard().autoConfig(
HealthIndicatorAutoConfiguration.class, ManagementServerProperties.class);
@Test
public void defaultHealthIndicator() {

View File

@ -57,21 +57,18 @@ public class Neo4jHealthIndicatorTests {
@Test
public void neo4jUp() {
Result result = mock(Result.class);
given(this.session.query(Neo4jHealthIndicator.CYPHER, Collections.EMPTY_MAP))
given(this.session.query(Neo4jHealthIndicator.CYPHER, Collections.emptyMap()))
.willReturn(result);
int nodeCount = 500;
Map<String, Object> expectedCypherDetails = new HashMap<>();
expectedCypherDetails.put("nodes", nodeCount);
List<Map<String, Object>> queryResults = new ArrayList<>();
queryResults.add(expectedCypherDetails);
given(result.queryResults()).willReturn(queryResults);
Health health = this.neo4jHealthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> details = health.getDetails();
int nodeCountFromDetails = (int) details.get("nodes");
Assert.assertEquals(nodeCount, nodeCountFromDetails);
}
@ -80,9 +77,8 @@ public class Neo4jHealthIndicatorTests {
CypherException cypherException = new CypherException("Error executing Cypher",
"Neo.ClientError.Statement.SyntaxError",
"Unable to execute invalid Cypher");
given(this.session.query(Neo4jHealthIndicator.CYPHER, Collections.EMPTY_MAP))
given(this.session.query(Neo4jHealthIndicator.CYPHER, Collections.emptyMap()))
.willThrow(cypherException);
Health health = this.neo4jHealthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
}

View File

@ -71,16 +71,13 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
private JedisConnectionFactory createJedisConnectionFactory() {
JedisClientConfiguration clientConfiguration = getJedisClientConfiguration();
if (getSentinelConfig() != null) {
return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration);
}
if (getClusterConfiguration() != null) {
return new JedisConnectionFactory(getClusterConfiguration(),
clientConfiguration);
}
return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration);
}

View File

@ -145,16 +145,13 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
private LettuceConnectionFactory createLettuceConnectionFactory(
LettuceClientConfiguration clientConfiguration) {
if (getSentinelConfig() != null) {
return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration);
}
if (getClusterConfiguration() != null) {
return new LettuceConnectionFactory(getClusterConfiguration(),
clientConfiguration);
}
return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
}

View File

@ -54,7 +54,6 @@ abstract class RedisConnectionConfiguration {
protected final RedisStandaloneConfiguration getStandaloneConfig() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
if (StringUtils.hasText(this.properties.getUrl())) {
ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
config.setHostName(connectionInfo.getHostName());
@ -149,7 +148,9 @@ abstract class RedisConnectionConfiguration {
protected static class ConnectionInfo {
private final URI uri;
private final boolean useSsl;
private final String password;
public ConnectionInfo(URI uri, boolean useSsl, String password) {

View File

@ -32,15 +32,20 @@ import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.util.MimeType;
/**
* {@link EnableAutoConfiguration Auto-configuration}
* for {@link org.springframework.core.codec.Encoder}s and {@link org.springframework.core.codec.Decoder}s.
* {@link EnableAutoConfiguration Auto-configuration} for
* {@link org.springframework.core.codec.Encoder Encoders} and
* {@link org.springframework.core.codec.Decoder Decoders}.
*
* @author Brian Clozel
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(CodecConfigurer.class)
@AutoConfigureAfter(JacksonAutoConfiguration.class)
public class CodecsAutoConfiguration {
private static final MimeType[] EMPTY_MIME_TYPES = {};
@Configuration
@ConditionalOnClass(ObjectMapper.class)
static class JacksonCodecConfiguration {
@ -48,10 +53,12 @@ public class CodecsAutoConfiguration {
@Bean
@ConditionalOnBean(ObjectMapper.class)
public CodecCustomizer jacksonCodecCustomizer(ObjectMapper objectMapper) {
return configurer -> {
return (configurer) -> {
CodecConfigurer.DefaultCodecs defaults = configurer.defaultCodecs();
defaults.jackson2Decoder(new Jackson2JsonDecoder(objectMapper, new MimeType[0]));
defaults.jackson2Encoder(new Jackson2JsonEncoder(objectMapper, new MimeType[0]));
defaults.jackson2Decoder(
new Jackson2JsonDecoder(objectMapper, EMPTY_MIME_TYPES));
defaults.jackson2Encoder(
new Jackson2JsonEncoder(objectMapper, EMPTY_MIME_TYPES));
};
}

View File

@ -55,8 +55,7 @@ class DataSourceJmxConfiguration {
private final ObjectProvider<MBeanExporter> mBeanExporter;
Hikari(HikariDataSource dataSource,
ObjectProvider<MBeanExporter> mBeanExporter) {
Hikari(HikariDataSource dataSource, ObjectProvider<MBeanExporter> mBeanExporter) {
this.dataSource = dataSource;
this.mBeanExporter = mBeanExporter;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -82,7 +82,8 @@ import org.springframework.web.reactive.result.view.ViewResolver;
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnClass(WebFluxConfigurer.class)
@ConditionalOnMissingBean({ WebFluxConfigurationSupport.class })
@AutoConfigureAfter({ ReactiveWebServerAutoConfiguration.class, CodecsAutoConfiguration.class })
@AutoConfigureAfter({ ReactiveWebServerAutoConfiguration.class,
CodecsAutoConfiguration.class })
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class WebFluxAutoConfiguration {
@ -133,7 +134,8 @@ public class WebFluxAutoConfiguration {
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
if (this.codecCustomizers != null) {
this.codecCustomizers.forEach(codecCustomizer -> codecCustomizer.customize(configurer));
this.codecCustomizers
.forEach((customizer) -> customizer.customize(configurer));
}
}

View File

@ -38,8 +38,11 @@ import org.springframework.web.reactive.function.client.WebClient;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link WebClient}.
* <p>This will produce a {@link WebClient.Builder} bean with the {@code prototype} scope,
* meaning each injection point will receive a newly cloned instance of the builder.
* <p>
* This will produce a
* {@link org.springframework.web.reactive.function.client.WebClient.Builder
* WebClient.Builder} bean with the {@code prototype} scope, meaning each injection point
* will receive a newly cloned instance of the builder.
*
* @author Brian Clozel
* @since 2.0.0
@ -51,14 +54,15 @@ public class WebClientAutoConfiguration {
private final WebClient.Builder webClientBuilder;
public WebClientAutoConfiguration(ObjectProvider<List<WebClientCustomizer>> customizerProvider) {
public WebClientAutoConfiguration(
ObjectProvider<List<WebClientCustomizer>> customizerProvider) {
this.webClientBuilder = WebClient.builder();
List<WebClientCustomizer> customizers = customizerProvider.getIfAvailable();
if (!CollectionUtils.isEmpty(customizers)) {
customizers = new ArrayList<>(customizers);
AnnotationAwareOrderComparator.sort(customizers);
customizers.forEach(customizer -> customizer.customize(this.webClientBuilder));
customizers
.forEach(customizer -> customizer.customize(this.webClientBuilder));
}
}

View File

@ -25,6 +25,7 @@ import org.springframework.web.reactive.function.client.WebClient;
/**
* {@link WebClientCustomizer} that configures codecs for the HTTP client.
*
* @author Brian Clozel
* @since 2.0.0
*/
@ -39,9 +40,10 @@ public class WebClientCodecCustomizer implements WebClientCustomizer {
@Override
public void customize(WebClient.Builder webClientBuilder) {
webClientBuilder
.exchangeStrategies(ExchangeStrategies.builder()
.codecs(codecs -> {
this.codecCustomizers.forEach(codecCustomizer -> codecCustomizer.customize(codecs));
}).build());
.exchangeStrategies(ExchangeStrategies.builder().codecs(codecs -> {
this.codecCustomizers
.forEach((customizer) -> customizer.customize(codecs));
}).build());
}
}

View File

@ -1076,54 +1076,63 @@ public class CacheAutoConfigurationTests {
@Bean
public CacheManagerCustomizer<CacheManager> allCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<CacheManager>() {
};
}
@Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> simpleCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<ConcurrentMapCacheManager>() {
};
}
@Bean
public CacheManagerCustomizer<SimpleCacheManager> genericCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<SimpleCacheManager>() {
};
}
@Bean
public CacheManagerCustomizer<CouchbaseCacheManager> couchbaseCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<CouchbaseCacheManager>() {
};
}
@Bean
public CacheManagerCustomizer<RedisCacheManager> redisCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<RedisCacheManager>() {
};
}
@Bean
public CacheManagerCustomizer<EhCacheCacheManager> ehcacheCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<EhCacheCacheManager>() {
};
}
@Bean
public CacheManagerCustomizer<HazelcastCacheManager> hazelcastCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<HazelcastCacheManager>() {
};
}
@Bean
public CacheManagerCustomizer<SpringEmbeddedCacheManager> infinispanCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<SpringEmbeddedCacheManager>() {
};
}
@Bean
public CacheManagerCustomizer<CaffeineCacheManager> caffeineCacheManagerCustomizer() {
return new CacheManagerTestCustomizer<CaffeineCacheManager>() {
};
}

View File

@ -146,8 +146,8 @@ public class CassandraDataAutoConfigurationTests {
@Bean
public CassandraCustomConversions myCassandraCustomConversions() {
return new CassandraCustomConversions(Collections.singletonList(
new MyConverter()));
return new CassandraCustomConversions(
Collections.singletonList(new MyConverter()));
}
}

View File

@ -92,16 +92,16 @@ public class MixedNeo4jRepositoriesAutoConfigurationTests {
}
private void load(Class<?> config, String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
TestPropertyValues.of(environment).and("spring.datasource.initialize", "false")
.and("spring.data.neo4j.uri", "http://localhost:8989").applyTo(ctx);
ctx.register(config);
ctx.register(DataSourceAutoConfiguration.class,
.and("spring.data.neo4j.uri", "http://localhost:8989").applyTo(context);
context.register(config);
context.register(DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class, Neo4jDataAutoConfiguration.class,
Neo4jRepositoriesAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
context.refresh();
this.context = context;
}
@Configuration

View File

@ -121,18 +121,19 @@ public class RedisAutoConfigurationJedisTests {
List<String> sentinels = Arrays.asList("127.0.0.1:26379", "127.0.0.1:26380");
load("spring.redis.sentinel.master:mymaster", "spring.redis.sentinel.nodes:"
+ StringUtils.collectionToCommaDelimitedString(sentinels));
assertThat(this.context.getBean(JedisConnectionFactory.class)
.isRedisSentinelAware()).isTrue();
assertThat(
this.context.getBean(JedisConnectionFactory.class).isRedisSentinelAware())
.isTrue();
}
@Test
public void testRedisConfigurationWithSentinelAndPassword() {
List<String> sentinels = Arrays.asList("127.0.0.1:26379", "127.0.0.1:26380");
load("spring.redis.password=password", "spring.redis.sentinel.master:mymaster",
"spring.redis.sentinel.nodes:" + StringUtils
.collectionToCommaDelimitedString(sentinels));
assertThat(this.context.getBean(JedisConnectionFactory.class)
.getPassword()).isEqualTo("password");
"spring.redis.sentinel.nodes:"
+ StringUtils.collectionToCommaDelimitedString(sentinels));
assertThat(this.context.getBean(JedisConnectionFactory.class).getPassword())
.isEqualTo("password");
}
@Test
@ -140,8 +141,9 @@ public class RedisAutoConfigurationJedisTests {
List<String> clusterNodes = Arrays.asList("127.0.0.1:27379", "127.0.0.1:27380");
load("spring.redis.cluster.nodes[0]:" + clusterNodes.get(0),
"spring.redis.cluster.nodes[1]:" + clusterNodes.get(1));
assertThat(this.context.getBean(JedisConnectionFactory.class)
.getClusterConnection()).isNotNull();
assertThat(
this.context.getBean(JedisConnectionFactory.class).getClusterConnection())
.isNotNull();
}
private void load(String... environment) {
@ -149,14 +151,14 @@ public class RedisAutoConfigurationJedisTests {
}
private void load(Class<?> config, String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
TestPropertyValues.of(environment).applyTo(ctx);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
TestPropertyValues.of(environment).applyTo(context);
if (config != null) {
ctx.register(config);
context.register(config);
}
ctx.register(RedisAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
context.register(RedisAutoConfiguration.class);
context.refresh();
this.context = context;
}
@Configuration

View File

@ -154,8 +154,8 @@ public class RedisAutoConfigurationTests {
load("spring.redis.password=password", "spring.redis.sentinel.master:mymaster",
"spring.redis.sentinel.nodes:"
+ StringUtils.collectionToCommaDelimitedString(sentinels));
assertThat(this.context.getBean(LettuceConnectionFactory.class)
.getPassword()).isEqualTo("password");
assertThat(this.context.getBean(LettuceConnectionFactory.class).getPassword())
.isEqualTo("password");
}
@Test
@ -173,8 +173,8 @@ public class RedisAutoConfigurationTests {
load("spring.redis.password=password",
"spring.redis.cluster.nodes[0]:" + clusterNodes.get(0),
"spring.redis.cluster.nodes[1]:" + clusterNodes.get(1));
assertThat(this.context.getBean(LettuceConnectionFactory.class)
.getPassword()).isEqualTo("password");
assertThat(this.context.getBean(LettuceConnectionFactory.class).getPassword())
.isEqualTo("password");
}
private DefaultLettucePool getDefaultLettucePool(LettuceConnectionFactory factory) {

View File

@ -88,8 +88,7 @@ public class DataSourceJmxConfigurationTests {
public void hikariAutoConfiguredUsesJmsFlag() throws MalformedObjectNameException {
String poolName = UUID.randomUUID().toString();
load("spring.datasource.type=" + HikariDataSource.class.getName(),
"spring.jmx.enabled=false",
"spring.datasource.name=" + poolName,
"spring.jmx.enabled=false", "spring.datasource.name=" + poolName,
"spring.datasource.hikari.register-mbeans=true");
assertThat(this.context.getBeansOfType(HikariDataSource.class)).hasSize(1);
assertThat(this.context.getBean(HikariDataSource.class).isRegisterMbeans())
@ -101,10 +100,12 @@ public class DataSourceJmxConfigurationTests {
private void validateHikariMBeansRegistration(MBeanServer mBeanServer,
String poolName, boolean expected) throws MalformedObjectNameException {
assertThat(mBeanServer.isRegistered(new ObjectName(
"com.zaxxer.hikari:type=Pool (" + poolName + ")"))).isEqualTo(expected);
assertThat(mBeanServer.isRegistered(new ObjectName(
"com.zaxxer.hikari:type=PoolConfig (" + poolName + ")"))).isEqualTo(expected);
assertThat(mBeanServer.isRegistered(
new ObjectName("com.zaxxer.hikari:type=Pool (" + poolName + ")")))
.isEqualTo(expected);
assertThat(mBeanServer.isRegistered(
new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + poolName + ")")))
.isEqualTo(expected);
}
@Test
@ -127,16 +128,16 @@ public class DataSourceJmxConfigurationTests {
}
private void load(Class<?> config, String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
String jdbcUrl = "jdbc:hsqldb:mem:test-" + UUID.randomUUID().toString();
TestPropertyValues.of(environment).and("spring.datasource.url", jdbcUrl)
.applyTo(ctx);
.applyTo(context);
if (config != null) {
ctx.register(config);
context.register(config);
}
ctx.register(JmxAutoConfiguration.class, DataSourceAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
context.register(JmxAutoConfiguration.class, DataSourceAutoConfiguration.class);
context.refresh();
this.context = context;
}
@Configuration

View File

@ -31,7 +31,7 @@ import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link HikariDataSourceConfiguration}.
* Tests for {@link DataSourceAutoConfiguration} with Hikari.
*
* @author Dave Syer
* @author Stephane Nicoll
@ -104,8 +104,7 @@ public class HikariDataSourceConfigurationTests {
private void load(String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
TestPropertyValues.of(environment)
.and("spring.datasource.initialize", "false")
TestPropertyValues.of(environment).and("spring.datasource.initialize", "false")
.and("spring.datasource.type", HikariDataSource.class.getName())
.applyTo(ctx);
ctx.register(DataSourceAutoConfiguration.class);

View File

@ -81,13 +81,14 @@ public abstract class AbstractJpaAutoConfigurationTests {
@Test
public void testNoDataSource() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(PropertyPlaceholderAutoConfiguration.class,
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(PropertyPlaceholderAutoConfiguration.class,
getAutoConfigureClass());
this.expected.expect(BeanCreationException.class);
this.expected.expectMessage("No qualifying bean");
this.expected.expectMessage("DataSource");
ctx.refresh();
context.refresh();
context.close();
}
@Test
@ -200,7 +201,8 @@ public abstract class AbstractJpaAutoConfigurationTests {
load(configs, new Class<?>[0], environment);
}
protected void load(Class<?>[] configs, Class<?>[] autoConfigs, String... environment) {
protected void load(Class<?>[] configs, Class<?>[] autoConfigs,
String... environment) {
load(configs, autoConfigs, null, environment);
}
@ -211,14 +213,13 @@ public abstract class AbstractJpaAutoConfigurationTests {
ctx.setClassLoader(classLoader);
}
TestPropertyValues.of(environment)
.and("spring.datasource.generate-unique-name", "true")
.applyTo(ctx);
.and("spring.datasource.generate-unique-name", "true").applyTo(ctx);
ctx.register(TestConfiguration.class);
if (!ObjectUtils.isEmpty(configs)) {
ctx.register(configs);
}
ctx.register(
DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class,
ctx.register(DataSourceAutoConfiguration.class,
TransactionAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, getAutoConfigureClass());
if (!ObjectUtils.isEmpty(autoConfigs)) {
ctx.register(autoConfigs);

View File

@ -84,7 +84,8 @@ public class HibernateJpaAutoConfigurationTests
@Test
public void testDataScript() throws Exception {
// This can't succeed because the data SQL is executed immediately after the schema
// This can't succeed because the data SQL is executed immediately after the
// schema
// and Hibernate hasn't initialized yet at that point
this.thrown.expect(BeanCreationException.class);
load("spring.datasource.data:classpath:/city.sql");
@ -93,12 +94,11 @@ public class HibernateJpaAutoConfigurationTests
@Test
public void testDataScriptRunsEarly() {
load(new Class<?>[] { TestInitializedJpaConfiguration.class }, null,
new HideDataScriptClassLoader(),
"spring.jpa.show-sql=true",
new HideDataScriptClassLoader(), "spring.jpa.show-sql=true",
"spring.jpa.hibernate.ddl-auto:create-drop",
"spring.datasource.data:classpath:/city.sql");
assertThat(this.context.getBean(
TestInitializedJpaConfiguration.class).called).isTrue();
assertThat(this.context.getBean(TestInitializedJpaConfiguration.class).called)
.isTrue();
}
@Test
@ -158,7 +158,8 @@ public class HibernateJpaAutoConfigurationTests
@Autowired
public void validateDataSourceIsInitialized(
EntityManagerFactory entityManagerFactory) {
// Inject the entity manager to validate it is initialized at the injection point
// Inject the entity manager to validate it is initialized at the injection
// point
EntityManager entityManager = entityManagerFactory.createEntityManager();
City city = entityManager.find(City.class, 2000L);
assertThat(city).isNotNull();
@ -204,21 +205,21 @@ public class HibernateJpaAutoConfigurationTests
private static class HideDataScriptClassLoader extends URLClassLoader {
private static final List<String> HIDDEN_RESOURCES =
Arrays.asList("schema-all.sql", "schema.sql");
private static final List<String> HIDDEN_RESOURCES = Arrays
.asList("schema-all.sql", "schema.sql");
HideDataScriptClassLoader() {
super(new URL[0], HideDataScriptClassLoader.class.getClassLoader());
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
if (HIDDEN_RESOURCES.contains(name)) {
return new Vector().elements();
return new Vector<URL>().elements();
}
return super.getResources(name);
}
}
}

View File

@ -21,8 +21,9 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
@ -45,11 +46,11 @@ public class OAuth2RestOperationsConfigurationTests {
@Test
public void clientIdConditionMatches() throws Exception {
EnvironmentTestUtils.addEnvironment(this.environment,
"security.oauth2.client.client-id=acme");
TestPropertyValues.of("security.oauth2.client.client-id=acme")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(
OAuth2RestOperationsConfiguration.class).environment(this.environment)
.web(false).run();
.web(WebApplicationType.NONE).run();
assertThat(this.context.getBean(OAuth2RestOperationsConfiguration.class))
.isNotNull();
}
@ -58,7 +59,7 @@ public class OAuth2RestOperationsConfigurationTests {
public void clientIdConditionDoesNotMatch() throws Exception {
this.context = new SpringApplicationBuilder(
OAuth2RestOperationsConfiguration.class).environment(this.environment)
.web(false).run();
.web(WebApplicationType.NONE).run();
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.context.getBean(OAuth2RestOperationsConfiguration.class);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -107,20 +107,20 @@ public class WebFluxAutoConfigurationTests {
load(CustomArgumentResolvers.class);
RequestMappingHandlerAdapter adapter = this.context
.getBean(RequestMappingHandlerAdapter.class);
assertThat((List<HandlerMethodArgumentResolver>) ReflectionTestUtils
.getField(adapter.getArgumentResolverConfigurer(), "customResolvers"))
.contains(
this.context.getBean("firstResolver",
HandlerMethodArgumentResolver.class),
this.context.getBean("secondResolver",
HandlerMethodArgumentResolver.class));
List<HandlerMethodArgumentResolver> customResolvers = (List<HandlerMethodArgumentResolver>) ReflectionTestUtils
.getField(adapter.getArgumentResolverConfigurer(), "customResolvers");
assertThat(customResolvers).contains(
this.context.getBean("firstResolver",
HandlerMethodArgumentResolver.class),
this.context.getBean("secondResolver",
HandlerMethodArgumentResolver.class));
}
@Test
public void shouldCustomizeCodecs() throws Exception {
load(CustomCodecCustomizers.class);
CodecCustomizer codecCustomizer =
this.context.getBean("firstCodecCustomizer", CodecCustomizer.class);
CodecCustomizer codecCustomizer = this.context.getBean("firstCodecCustomizer",
CodecCustomizer.class);
assertThat(codecCustomizer).isNotNull();
verify(codecCustomizer).customize(any(ServerCodecConfigurer.class));
}
@ -133,7 +133,6 @@ public class WebFluxAutoConfigurationTests {
assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class);
ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap().get("/**");
assertThat(staticHandler.getLocations()).hasSize(5);
assertThat(hm.getUrlMap().get("/webjars/**"))
.isInstanceOf(ResourceWebHandler.class);
ResourceWebHandler webjarsHandler = (ResourceWebHandler) hm.getUrlMap()

View File

@ -61,7 +61,8 @@ public class WebClientAutoConfigurationTests {
load(CodecConfiguration.class);
WebClient.Builder builder = this.context.getBean(WebClient.Builder.class);
CodecCustomizer codecCustomizer = this.context.getBean(CodecCustomizer.class);
WebClientCodecCustomizer clientCustomizer = this.context.getBean(WebClientCodecCustomizer.class);
WebClientCodecCustomizer clientCustomizer = this.context
.getBean(WebClientCodecCustomizer.class);
builder.build();
assertThat(clientCustomizer).isNotNull();
verify(codecCustomizer).customize(any(CodecConfigurer.class));
@ -79,24 +80,22 @@ public class WebClientAutoConfigurationTests {
@Test
public void shouldGetPrototypeScopedBean() throws Exception {
load(WebClientCustomizerConfig.class);
ClientHttpConnector firstConnector = mock(ClientHttpConnector.class);
given(firstConnector.connect(any(), any(), any())).willReturn(Mono.empty());
WebClient.Builder firstBuilder = this.context.getBean(WebClient.Builder.class);
firstBuilder.clientConnector(firstConnector).baseUrl("http://first.example.org");
ClientHttpConnector secondConnector = mock(ClientHttpConnector.class);
given(secondConnector.connect(any(), any(), any())).willReturn(Mono.empty());
WebClient.Builder secondBuilder = this.context.getBean(WebClient.Builder.class);
secondBuilder.clientConnector(secondConnector).baseUrl("http://second.example.org");
secondBuilder.clientConnector(secondConnector)
.baseUrl("http://second.example.org");
assertThat(firstBuilder).isNotEqualTo(secondBuilder);
firstBuilder.build().get().uri("/foo").exchange().block();
secondBuilder.build().get().uri("/foo").exchange().block();
verify(firstConnector).connect(eq(HttpMethod.GET), eq(URI.create("http://first.example.org/foo")), any());
verify(secondConnector).connect(eq(HttpMethod.GET), eq(URI.create("http://second.example.org/foo")), any());
verify(firstConnector).connect(eq(HttpMethod.GET),
eq(URI.create("http://first.example.org/foo")), any());
verify(secondConnector).connect(eq(HttpMethod.GET),
eq(URI.create("http://second.example.org/foo")), any());
WebClientCustomizer customizer = this.context.getBean(WebClientCustomizer.class);
verify(customizer, times(1)).customize(any(WebClient.Builder.class));
}
@ -108,7 +107,6 @@ public class WebClientAutoConfigurationTests {
assertThat(builder).isInstanceOf(MyWebClientBuilder.class);
}
private void load(Class<?>... config) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(config);
@ -124,6 +122,7 @@ public class WebClientAutoConfigurationTests {
public CodecCustomizer myCodecCustomizer() {
return mock(CodecCustomizer.class);
}
}
@Configuration

View File

@ -498,9 +498,9 @@ The Spring Boot starters bring a default embedded container for you:
* `spring-boot-starter-web` brings Tomcat with `spring-boot-starter-tomcat`,
but `spring-boot-starter-jetty` and `spring-boot-starter-undertow` can be used instead.
* `spring-boot-starter-webflux` brings Reactor Netty with `spring-boot-starter-reactor-netty`,
but `spring-boot-starter-tomcat`, `spring-boot-starter-jetty` and
`spring-boot-starter-undertow` can be used instead.
* `spring-boot-starter-webflux` brings Reactor Netty with
`spring-boot-starter-reactor-netty`, but `spring-boot-starter-tomcat`,
`spring-boot-starter-jetty` and `spring-boot-starter-undertow` can be used instead.
NOTE: Many starters only support Spring MVC, so they transitively bring
`spring-boot-starter-web` into your application classpath
@ -548,8 +548,10 @@ Example in Gradle, for Spring WebFlux:
}
----
NOTE: `spring-boot-starter-reactor-netty` is required to use the `WebClient`,
so excluding it is not required if you wish to use a different HTTP server.
NOTE: `spring-boot-starter-reactor-netty` is required to use the `WebClient`, so excluding
it is not required if you wish to use a different HTTP server.
[[howto-configure-jetty]]
=== Configure Jetty

View File

@ -2187,6 +2187,8 @@ method:
}
----
[[boot-features-spring-webflux]]
=== The '`Spring WebFlux framework`'
@ -2195,8 +2197,8 @@ Unlike Spring MVC, it does not require the Servlet API, is fully asynchronous an
non-blocking, and implements the http://www.reactive-streams.org/[Reactive Streams]
specification through http://projectreactor.io/[the Reactor project].
Spring WebFlux comes in two flavours — the annotation-based one is quite close to
the Spring MVC model we know:
Spring WebFlux comes in two flavors — the annotation-based one is quite close to the
Spring MVC model we know:
[source,java,indent=0]
----
@ -2222,8 +2224,8 @@ the Spring MVC model we know:
}
----
'`WebFlux.fn`', the functional variant, separates the routing configuration from the actual handling
of the requests:
'`WebFlux.fn`', the functional variant, separates the routing configuration from the
actual handling of the requests:
[source,java,indent=0]
----
@ -2236,17 +2238,17 @@ of the requests:
.andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
.andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
}
}
@Component
public class UserHandler {
public Mono<ServerResponse> getUser(ServerRequest request) {
// ...
}
public Mono<ServerResponse> getUser(ServerRequest request) {
// ...
}
public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
// ...
}
@ -2256,19 +2258,20 @@ of the requests:
}
----
WebFlux is part of the Spring Framework and detailed information is available in
the {spring-reference}web.html#web-reactive[reference documentation].
WebFlux is part of the Spring Framework and detailed information is available in the
{spring-reference}web.html#web-reactive[reference documentation].
To get started, add the `spring-boot-starter-webflux` module to your application.
NOTE: Adding both `spring-boot-starter-web` and `spring-boot-starter-webflux`
modules in your application will result in Spring Boot auto-configuring Spring
MVC, not WebFlux. This behavior has been chosen because many Spring developers
will add `spring-boot-starter-webflux` to their Spring MVC application to use
the reactive `WebCLient`. You can still enforce your choice by setting the
chosen application type like
NOTE: Adding both `spring-boot-starter-web` and `spring-boot-starter-webflux` modules in
your application will result in Spring Boot auto-configuring Spring MVC, not WebFlux. This
behavior has been chosen because many Spring developers will add
`spring-boot-starter-webflux` to their Spring MVC application to use the reactive
`WebCLient`. You can still enforce your choice by setting the chosen application type like
`SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)`.
[[boot-features-spring-webflux-auto-configuration]]
==== Spring WebFlux auto-configuration
Spring Boot provides auto-configuration for Spring WebFlux that works well with most
@ -2276,29 +2279,30 @@ applications.
The auto-configuration adds the following features on top of Spring's defaults:
* Configuring codecs for `HttpMessageReader` and `HttpMessageWriter` instances (see below).
* Configuring codecs for `HttpMessageReader` and `HttpMessageWriter` instances (see
below).
* Support for serving static resources, including support for WebJars (see below).
If you want to keep Spring Boot WebFlux features, and you just want to add additional
{spring-reference}web.html#web-reactive[WebFlux configuration] you can add your own
`@Configuration` class of type `WebFluxConfigurer`, but *without* `@EnableWebFlux`.
If you want to keep Spring Boot WebFlux features, and
you just want to add additional {spring-reference}web.html#web-reactive[WebFlux configuration]
you can add your own `@Configuration` class of type `WebFluxConfigurer`,
but *without* `@EnableWebFlux`.
If you want to take complete control of Spring WebFlux, you can add your own
`@Configuration` annotated with `@EnableWebFlux`.
If you want to take complete control of Spring WebFlux, you can add your own `@Configuration`
annotated with `@EnableWebFlux`.
[[boot-features-spring-webflux-httpcodecs]]
==== HTTP codecs with HttpMessageReaders and HttpMessageWriters
Spring WebFlux uses the `HttpMessageReader` and `HttpMessageWriter` interface to convert
HTTP requests and responses. They are configured with `CodecConfigurer` with sensible defaults,
by looking at the libraries available in your classpath.
HTTP requests and responses. They are configured with `CodecConfigurer` with sensible
defaults, by looking at the libraries available in your classpath.
Spring Boot will apply further customization using `CodecCustomizer` instances.
For example, `spring.jackson.*` configuration keys will be applied to the Jackson codec.
If you need to add or customize codecs you can create a custom `CodecCustomizer` component:
If you need to add or customize codecs you can create a custom `CodecCustomizer`
component:
[source,java,indent=0]
----
@ -2319,6 +2323,8 @@ If you need to add or customize codecs you can create a custom `CodecCustomizer`
You can also leverage <<boot-features-json-components,Boot's custom JSON serializers and deserializers>>.
[[boot-features-spring-webflux-static-content]]
==== Static Content
By default Spring Boot will serve static content from a directory called `/static` (or
@ -2353,9 +2359,9 @@ be deployed as war and have no use of the `src/main/webapp` directory.
[[boot-features-spring-webflux-template-engines]]
==== Template engines
As well as REST web services, you can also use Spring WebFlux to serve dynamic HTML content.
Spring WebFlux supports a variety of templating technologies including Thymeleaf, FreeMarker
and Mustache.
As well as REST web services, you can also use Spring WebFlux to serve dynamic HTML
content. Spring WebFlux supports a variety of templating technologies including Thymeleaf,
FreeMarker and Mustache.
Spring Boot includes auto-configuration support for the following templating engines:
@ -2367,6 +2373,7 @@ When you're using one of these templating engines with the default configuration
templates will be picked up automatically from `src/main/resources/templates`.
[[boot-features-jersey]]
=== JAX-RS and Jersey
If you prefer the JAX-RS programming model for REST endpoints you can use one of the
@ -5094,6 +5101,8 @@ TIP: `RestTemplateBuilder` includes a number of useful methods that can be used
configure a `RestTemplate`. For example, to add BASIC auth support you can use
`builder.basicAuthorization("user", "password").build()`.
[[boot-features-resttemplate-customization]]
=== RestTemplate customization
There are three main approaches to `RestTemplate` customization, depending on how broadly
@ -5121,9 +5130,9 @@ Lastly, the most extreme (and rarely used) option is to create your own
`RestTemplateBuilder` and will prevent any `RestTemplateCustomizer` beans from being used.
[[boot-features-webclient]]
== Calling REST services with '`WebClient`'
If you have Spring WebFlux on your classpath, you can also choose to use `WebClient`
to call remote REST services; compared to `RestTemplate`, this client has more a
functional feel to it and is fully reactive. You can create your own client
@ -5176,6 +5185,8 @@ would do locally at the point of injection.
Lastly, you can fall back to the original API and just use `WebClient.create()`. In that
case, no auto-configuration nor `WebClientCustomizer` will be applied.
[[boot-features-validation]]
== Validation
The method validation feature supported by Bean Validation 1.1 is automatically enabled
@ -5992,12 +6003,15 @@ definition.
A list of the auto-configuration that is enabled by `@WebMvcTest` can be
<<appendix-test-auto-configuration#test-auto-configuration,found in the appendix>>.
[[boot-features-testing-spring-boot-applications-testing-autoconfigured-webflux-tests]]
==== Auto-configured Spring WebFlux tests
To test Spring WebFlux controllers are working as expected you can use the `@WebFluxTest`
annotation. `@WebFluxTest` will auto-configure the Spring WebFlux infrastructure and limit
scanned beans to `@Controller`, `@ControllerAdvice`, `@JsonComponent`,and `WebFluxConfigurer`.
Regular `@Component` beans will not be scanned when using this annotation.
scanned beans to `@Controller`, `@ControllerAdvice`, `@JsonComponent`,and
`WebFluxConfigurer`. Regular `@Component` beans will not be scanned when using this
annotation.
Often `@WebFluxTest` will be limited to a single controller and used in combination with
`@MockBean` to provide mock implementations for required collaborators.
@ -6010,14 +6024,14 @@ TIP: You can also auto-configure `WebTestClient` in a non-`@WebFluxTest`
[source,java,indent=0]
----
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
@RunWith(SpringRunner.class)
@WebFluxTest(UserVehicleController.class)
@ -6046,6 +6060,7 @@ A list of the auto-configuration that is enabled by `@WebFluxTest` can be
<<appendix-test-auto-configuration#test-auto-configuration,found in the appendix>>.
[[boot-features-testing-spring-boot-applications-testing-autoconfigured-jpa-test]]
==== Auto-configured Data JPA tests
`@DataJpaTest` can be used if you want to test JPA applications. By default it will

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -40,7 +40,6 @@ import org.springframework.test.context.BootstrapWith;
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to Redis tests.
* <p>
*
* @author Jayaram Pradhan
* @since 2.0.0

View File

@ -38,31 +38,28 @@ import org.springframework.web.reactive.function.client.WebClient;
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass({WebClient.class, WebTestClient.class})
@ConditionalOnClass({ WebClient.class, WebTestClient.class })
@AutoConfigureAfter(CodecsAutoConfiguration.class)
public class WebTestClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public WebTestClient webTestClient(ApplicationContext applicationContext) {
WebTestClient.Builder clientBuilder = WebTestClient
WebTestClient.Builder builder = WebTestClient
.bindToApplicationContext(applicationContext).configureClient();
customizeWebTestClientCodecs(clientBuilder, applicationContext);
return clientBuilder.build();
customizeWebTestClientCodecs(builder, applicationContext);
return builder.build();
}
private void customizeWebTestClientCodecs(WebTestClient.Builder clientBuilder,
private void customizeWebTestClientCodecs(WebTestClient.Builder builder,
ApplicationContext applicationContext) {
Collection<CodecCustomizer> codecCustomizers = applicationContext
.getBeansOfType(CodecCustomizer.class).values();
if (!CollectionUtils.isEmpty(codecCustomizers)) {
clientBuilder.exchangeStrategies(ExchangeStrategies.builder()
.codecs(codecs -> {
codecCustomizers.forEach(codecCustomizer -> codecCustomizer.customize(codecs));
})
.build());
builder.exchangeStrategies(ExchangeStrategies.builder().codecs((codecs) -> {
codecCustomizers
.forEach((codecCustomizer) -> codecCustomizer.customize(codecs));
}).build());
}
}

View File

@ -24,4 +24,5 @@ import org.springframework.data.repository.CrudRepository;
* @author Jayaram Pradhan
*/
public interface ExampleRepository extends CrudRepository<PersonHash, String> {
}

View File

@ -58,7 +58,6 @@ public class WebTestClientAutoConfigurationTests {
verify(codecCustomizer).customize(any(CodecConfigurer.class));
}
private void load(Class<?>... config) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(config);

View File

@ -20,6 +20,7 @@ import java.io.Closeable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
@ -49,7 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class AbstractContextLoader<T extends ConfigurableApplicationContext, L extends AbstractContextLoader<T, ?>>
implements ContextLoader {
private final Map<String, String> systemProperties = new HashMap<>();
private final Map<String, String> systemProperties = new LinkedHashMap<>();
private final List<String> env = new ArrayList<>();
@ -155,11 +156,11 @@ class AbstractContextLoader<T extends ConfigurableApplicationContext, L extends
return self();
}
@SuppressWarnings("unchecked")
protected final L self() {
return (L) this;
}
/**
* Create and refresh a new {@link ApplicationContext} based on the current state of
* this loader. The context is consumed by the specified {@link ContextConsumer} and
@ -226,13 +227,8 @@ class AbstractContextLoader<T extends ConfigurableApplicationContext, L extends
private T configureApplicationContext() {
T context = AbstractContextLoader.this.contextSupplier.get();
if (this.classLoader != null) {
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.classLoader);
}
else {
throw new IllegalStateException("Cannot configure ClassLoader: " + context
+ " is not a DefaultResourceLoader sub-class");
}
Assert.isInstanceOf(DefaultResourceLoader.class, context);
((DefaultResourceLoader) context).setClassLoader(this.classLoader);
}
if (!ObjectUtils.isEmpty(this.env)) {
TestPropertyValues.of(this.env.toArray(new String[this.env.size()]))

View File

@ -80,40 +80,6 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon
*/
public interface ContextLoader {
/**
* Creates a {@code ContextLoader} that will load a standard
* {@link AnnotationConfigApplicationContext}.
*
* @return the context loader
*/
static StandardContextLoader standard() {
return new StandardContextLoader(AnnotationConfigApplicationContext::new);
}
/**
* Creates a {@code ContextLoader} that will load a
* {@link AnnotationConfigWebApplicationContext}.
*
* @return the context loader
*/
static ServletWebContextLoader servletWeb() {
return new ServletWebContextLoader(() -> {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
return context;
});
}
/**
* Creates a {@code ContextLoader} that will load a
* {@link GenericReactiveWebApplicationContext}.
*
* @return the context loader
*/
static ReactiveWebContextLoader reactiveWeb() {
return new ReactiveWebContextLoader(GenericReactiveWebApplicationContext::new);
}
/**
* Set the specified system property prior to loading the context and restore its
* previous value once the consumer has been invoked and the context closed. If the
@ -197,4 +163,35 @@ public interface ContextLoader {
*/
<E extends Throwable> void loadAndFail(Class<E> exceptionType, Consumer<E> consumer);
/**
* Creates a {@code ContextLoader} that will load a standard
* {@link AnnotationConfigApplicationContext}.
* @return the context loader
*/
static StandardContextLoader standard() {
return new StandardContextLoader(AnnotationConfigApplicationContext::new);
}
/**
* Creates a {@code ContextLoader} that will load a
* {@link AnnotationConfigWebApplicationContext}.
* @return the context loader
*/
static ServletWebContextLoader servletWeb() {
return new ServletWebContextLoader(() -> {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
return context;
});
}
/**
* Creates a {@code ContextLoader} that will load a
* {@link GenericReactiveWebApplicationContext}.
* @return the context loader
*/
static ReactiveWebContextLoader reactiveWeb() {
return new ReactiveWebContextLoader(GenericReactiveWebApplicationContext::new);
}
}

View File

@ -28,8 +28,8 @@ import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicati
* @author Stephane Nicoll
* @since 2.0.0
*/
public final class ReactiveWebContextLoader
extends AbstractContextLoader<GenericReactiveWebApplicationContext, ReactiveWebContextLoader> {
public final class ReactiveWebContextLoader extends
AbstractContextLoader<GenericReactiveWebApplicationContext, ReactiveWebContextLoader> {
ReactiveWebContextLoader(
Supplier<GenericReactiveWebApplicationContext> contextSupplier) {

View File

@ -29,8 +29,8 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon
* @author Stephane Nicoll
* @since 2.0.0
*/
public final class ServletWebContextLoader
extends AbstractContextLoader<AnnotationConfigWebApplicationContext, ServletWebContextLoader> {
public final class ServletWebContextLoader extends
AbstractContextLoader<AnnotationConfigWebApplicationContext, ServletWebContextLoader> {
ServletWebContextLoader(
Supplier<AnnotationConfigWebApplicationContext> contextSupplier) {
@ -41,7 +41,8 @@ public final class ServletWebContextLoader
* Create and refresh a new {@link ConfigurableWebApplicationContext} based on the
* current state of this loader. The context is consumed by the specified
* {@link WebMvcContextConsumer consumer} and closed upon completion.
* @param consumer the consumer of the created {@link ConfigurableWebApplicationContext}
* @param consumer the consumer of the created
* {@link ConfigurableWebApplicationContext}
*/
public void loadWeb(WebMvcContextConsumer consumer) {
doLoad(consumer::accept);

View File

@ -27,10 +27,11 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
* @author Andy Wilkinson
* @since 2.0.0
*/
public class StandardContextLoader
extends AbstractContextLoader<AnnotationConfigApplicationContext, StandardContextLoader> {
public class StandardContextLoader extends
AbstractContextLoader<AnnotationConfigApplicationContext, StandardContextLoader> {
public StandardContextLoader(Supplier<AnnotationConfigApplicationContext> contextSupplier) {
public StandardContextLoader(
Supplier<AnnotationConfigApplicationContext> contextSupplier) {
super(contextSupplier);
}

View File

@ -59,7 +59,6 @@ class WebTestClientContextCustomizer implements ContextCustomizer {
if (beanFactory instanceof BeanDefinitionRegistry) {
registerWebTestClient(context, (BeanDefinitionRegistry) context);
}
}
private void registerWebTestClient(ConfigurableApplicationContext context,
@ -75,10 +74,7 @@ class WebTestClientContextCustomizer implements ContextCustomizer {
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
return true;
return (obj != null && obj.getClass().equals(getClass()));
}
/**
@ -139,13 +135,14 @@ class WebTestClientContextCustomizer implements ContextCustomizer {
private void customizeWebTestClientCodecs(WebTestClient.Builder clientBuilder,
ApplicationContext context) {
Collection<CodecCustomizer> codecCustomizers = context.getBeansOfType(CodecCustomizer.class).values();
Collection<CodecCustomizer> codecCustomizers = context
.getBeansOfType(CodecCustomizer.class).values();
if (!CollectionUtils.isEmpty(codecCustomizers)) {
clientBuilder.exchangeStrategies(ExchangeStrategies.builder()
.codecs(codecs -> {
codecCustomizers.forEach(codecCustomizer -> codecCustomizer.customize(codecs));
})
.build());
clientBuilder.exchangeStrategies(
ExchangeStrategies.builder().codecs(codecs -> {
codecCustomizers.forEach(
codecCustomizer -> codecCustomizer.customize(codecs));
}).build());
}
}

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>

View File

@ -151,7 +151,8 @@ public class RepackageMojo extends AbstractDependencyFilterMojo {
/**
* Make a fully executable jar for *nix machines by prepending a launch script to the
* jar. <br/>
* jar.
* <p>
* Currently, some tools do not accept this format so you may not always be able to
* use this technique. For example, <code>jar -xf</code> may silently fail to extract
* a jar or war that has been made fully-executable. It is recommended that you only

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -62,6 +62,7 @@ public enum CloudPlatform {
public boolean isActive(Environment environment) {
return environment.containsProperty("HC_LANDSCAPE");
}
};
/**

View File

@ -185,10 +185,8 @@ public final class ConfigurationPropertyName
start == 0,
() -> "Element value '" + elementValue + "' must be a single item"));
if (!isIndexed(elementValue)) {
List<Character> invalidChars = ElementValidator.getInvalidChars(elementValue);
if (!invalidChars.isEmpty()) {
throw new InvalidConfigurationPropertyNameException(invalidChars, elementValue);
}
InvalidConfigurationPropertyNameException.throwIfHasInvalidChars(elementValue,
ElementValidator.getInvalidChars(elementValue));
}
int length = this.elements.length;
CharSequence[] elements = new CharSequence[length + 1];
@ -447,7 +445,8 @@ public final class ConfigurationPropertyName
Assert.notNull(name, "Name must not be null");
if (name.length() >= 1
&& (name.charAt(0) == '.' || name.charAt(name.length() - 1) == '.')) {
throw new InvalidConfigurationPropertyNameException(Collections.singletonList('.'), name);
throw new InvalidConfigurationPropertyNameException(name,
Collections.singletonList('.'));
}
if (name.length() == 0) {
return EMPTY;
@ -456,10 +455,8 @@ public final class ConfigurationPropertyName
process(name, '.', (elementValue, start, end, indexed) -> {
if (elementValue.length() > 0) {
if (!indexed) {
List<Character> invalidChars = ElementValidator.getInvalidChars(elementValue);
if (!invalidChars.isEmpty()) {
throw new InvalidConfigurationPropertyNameException(invalidChars, name);
}
InvalidConfigurationPropertyNameException.throwIfHasInvalidChars(name,
ElementValidator.getInvalidChars(elementValue));
}
elements.add(elementValue);
}
@ -662,15 +659,6 @@ public final class ConfigurationPropertyName
return getInvalidChars(elementValue).isEmpty();
}
public static boolean isValidChar(char ch, int index) {
boolean isAlpha = ch >= 'a' && ch <= 'z';
boolean isNumeric = ch >= '0' && ch <= '9';
if (index == 0) {
return isAlpha;
}
return isAlpha || isNumeric || ch == '-';
}
private static List<Character> getInvalidChars(CharSequence elementValue) {
List<Character> chars = new ArrayList<>();
for (int i = 0; i < elementValue.length(); i++) {
@ -682,6 +670,15 @@ public final class ConfigurationPropertyName
return chars;
}
public static boolean isValidChar(char ch, int index) {
boolean isAlpha = ch >= 'a' && ch <= 'z';
boolean isNumeric = ch >= '0' && ch <= '9';
if (index == 0) {
return isAlpha;
}
return isAlpha || isNumeric || ch == '-';
}
}
}

View File

@ -19,22 +19,22 @@ package org.springframework.boot.context.properties.source;
import java.util.List;
/**
* Exception thrown when {@link ConfigurationPropertyName} has invalid
* characters.
* Exception thrown when {@link ConfigurationPropertyName} has invalid characters.
*
* @author Madhura Bhave
* @since 2.0.0
*/
public class InvalidConfigurationPropertyNameException extends RuntimeException {
private final List<Character> invalidCharacters;
private final CharSequence name;
public InvalidConfigurationPropertyNameException(List<Character> invalidCharacters, CharSequence name) {
private final List<Character> invalidCharacters;
public InvalidConfigurationPropertyNameException(CharSequence name,
List<Character> invalidCharacters) {
super("Configuration property name '" + name + "' is not valid");
this.invalidCharacters = invalidCharacters;
this.name = name;
this.invalidCharacters = invalidCharacters;
}
public List<Character> getInvalidCharacters() {
@ -45,5 +45,11 @@ public class InvalidConfigurationPropertyNameException extends RuntimeException
return this.name;
}
}
public static void throwIfHasInvalidChars(CharSequence name,
List<Character> invalidCharacters) {
if (!invalidCharacters.isEmpty()) {
throw new InvalidConfigurationPropertyNameException(name, invalidCharacters);
}
}
}

View File

@ -24,34 +24,44 @@ import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
/**
* An {@link AbstractFailureAnalyzer} that performs analysis of failures
* caused by {@link InvalidConfigurationPropertyNameException}.
* An {@link AbstractFailureAnalyzer} that performs analysis of failures caused by
* {@link InvalidConfigurationPropertyNameException}.
*
* @author Madhura Bhave
* @since 2.0.0
*/
public class InvalidConfigurationPropertyNameFailureAnalyzer extends AbstractFailureAnalyzer<InvalidConfigurationPropertyNameException> {
public class InvalidConfigurationPropertyNameFailureAnalyzer
extends AbstractFailureAnalyzer<InvalidConfigurationPropertyNameException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, InvalidConfigurationPropertyNameException cause) {
BeanCreationException exception = findCause(rootFailure, BeanCreationException.class);
String description = buildDescription(cause, exception);
String action = String.format("Modify '%s' so that it conforms to the canonical names requirements.", cause.getName());
return new FailureAnalysis(description, action, cause);
protected FailureAnalysis analyze(Throwable rootFailure,
InvalidConfigurationPropertyNameException cause) {
BeanCreationException exception = findCause(rootFailure,
BeanCreationException.class);
String action = String.format(
"Modify '%s' so that it conforms to the canonical names requirements.",
cause.getName());
return new FailureAnalysis(buildDescription(cause, exception), action, cause);
}
private String buildDescription(InvalidConfigurationPropertyNameException cause, BeanCreationException exception) {
StringBuilder description = new StringBuilder(
String.format("Configuration property name '%s' is not valid:%n", cause.getName()));
String invalid = cause.getInvalidCharacters().stream().map(c -> "'" + c.toString() + "'").collect(Collectors.joining(","));
private String buildDescription(InvalidConfigurationPropertyNameException cause,
BeanCreationException exception) {
StringBuilder description = new StringBuilder(String.format(
"Configuration property name '%s' is not valid:%n", cause.getName()));
String invalid = cause.getInvalidCharacters().stream().map(this::quote)
.collect(Collectors.joining(", "));
description.append(String.format("%n Invalid characters: %s", invalid));
if (exception != null) {
description.append(String.format("%n Bean: %s", exception.getBeanName()));
}
description.append(String.format("%n Reason: Canonical names should be kebab-case('-' separated), lowercase alpha-numeric characters" +
" and must start with a letter"));
description.append(String.format("%n Reason: Canonical names should be "
+ "kebab-case ('-' separated), lowercase alpha-numeric characters"
+ " and must start with a letter"));
return description.toString();
}
}
private String quote(Character c) {
return "'" + c + "'";
}
}

View File

@ -62,11 +62,12 @@ public class SystemEnvironmentPropertySourceEnvironmentPostProcessor
@SuppressWarnings("unchecked")
private void replacePropertySource(ConfigurableEnvironment environment,
String sourceName, PropertySource<?> propertySource) {
Map<String, Object> originalSource = (Map<String, Object>) propertySource
.getSource();
SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(sourceName, originalSource);
environment.getPropertySources().replace(sourceName, source);
}
Map<String, Object> originalSource = (Map<String, Object>) propertySource
.getSource();
SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(
sourceName, originalSource);
environment.getPropertySources().replace(sourceName, source);
}
@Override
public int getOrder() {
@ -80,10 +81,11 @@ public class SystemEnvironmentPropertySourceEnvironmentPostProcessor
/**
* {@link SystemEnvironmentPropertySource} that also tracks {@link Origin}.
*/
protected static class OriginAwareSystemEnvironmentPropertySource extends SystemEnvironmentPropertySource
implements OriginLookup<String> {
protected static class OriginAwareSystemEnvironmentPropertySource
extends SystemEnvironmentPropertySource implements OriginLookup<String> {
OriginAwareSystemEnvironmentPropertySource(String name, Map<String, Object> source) {
OriginAwareSystemEnvironmentPropertySource(String name,
Map<String, Object> source) {
super(name, source);
}
@ -95,6 +97,7 @@ public class SystemEnvironmentPropertySourceEnvironmentPostProcessor
}
return null;
}
}
}

View File

@ -19,8 +19,9 @@ package org.springframework.boot.web.codec;
import org.springframework.http.codec.CodecConfigurer;
/**
* Callback interface that can be used to customize codecs configuration
* for an HTTP client and/or server with a {@link CodecConfigurer}.
* Callback interface that can be used to customize codecs configuration for an HTTP
* client and/or server with a {@link CodecConfigurer}.
*
* @author Brian Clozel
* @since 2.0
*/

View File

@ -19,7 +19,9 @@ package org.springframework.boot.web.reactive.function.client;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Callback interface that can be used to customize a {@link WebClient.Builder}.
* Callback interface that can be used to customize a
* {@link org.springframework.web.reactive.function.client.WebClient.Builder
* WebClient.Builder}.
*
* @author Brian Clozel
* @since 2.0.0
@ -28,8 +30,11 @@ import org.springframework.web.reactive.function.client.WebClient;
public interface WebClientCustomizer {
/**
* Callback to customize a {@link WebClient.Builder} instance.
* Callback to customize a
* {@link org.springframework.web.reactive.function.client.WebClient.Builder
* WebClient.Builder} instance.
* @param webClientBuilder the client builder to customize
*/
void customize(WebClient.Builder webClientBuilder);
}

View File

@ -311,8 +311,7 @@ public class CollectionBinderTests {
source.put("foo.items", "a,b,c");
this.sources.add(source);
ExampleCollectionBean result = this.binder
.bind("foo", ExampleCollectionBean.class)
.get();
.bind("foo", ExampleCollectionBean.class).get();
assertThat(result.getItems()).hasSize(4);
assertThat(result.getItems()).containsExactly("a", "b", "c", "d");
}

View File

@ -37,13 +37,15 @@ public class InvalidConfigurationPropertyNameFailureAnalyzerTests {
private InvalidConfigurationPropertyNameFailureAnalyzer analyzer = new InvalidConfigurationPropertyNameFailureAnalyzer();
@Test
public void analysisWhenRootCauseIsBeanCreationFailureShouldContainBeanName() throws Exception {
public void analysisWhenRootCauseIsBeanCreationFailureShouldContainBeanName()
throws Exception {
BeanCreationException failure = createFailure(InvalidPrefixConfiguration.class);
FailureAnalysis analysis = this.analyzer.analyze(failure);
assertThat(analysis.getDescription()).contains(String.format(
"%n Invalid characters: %s%n Bean: %s%n Reason: %s",
"'F','P'", "invalidPrefixProperties", "Canonical names should be kebab-case('-' separated), " +
"lowercase alpha-numeric characters and must start with a letter"));
"%n Invalid characters: %s%n Bean: %s%n Reason: %s", "'F', 'P'",
"invalidPrefixProperties",
"Canonical names should be kebab-case ('-' separated), "
+ "lowercase alpha-numeric characters and must start with a letter"));
}
private BeanCreationException createFailure(Class<?> configuration) {
@ -81,6 +83,7 @@ public class InvalidConfigurationPropertyNameFailureAnalyzerTests {
public void setValue(String value) {
this.value = value;
}
}
}

View File

@ -52,7 +52,8 @@ public class SystemEnvironmentPropertySourceEnvironmentPostProcessorTests {
postProcessor.postProcessEnvironment(this.environment, null);
PropertySource<?> replaced = this.environment.getPropertySources()
.get("systemEnvironment");
assertThat(replaced).isInstanceOf(OriginAwareSystemEnvironmentPropertySource.class);
assertThat(replaced)
.isInstanceOf(OriginAwareSystemEnvironmentPropertySource.class);
}
@Test
@ -62,37 +63,39 @@ public class SystemEnvironmentPropertySourceEnvironmentPostProcessorTests {
PropertySource<?> original = this.environment.getPropertySources()
.get("systemEnvironment");
postProcessor.postProcessEnvironment(this.environment, null);
OriginAwareSystemEnvironmentPropertySource replaced = (OriginAwareSystemEnvironmentPropertySource) this.environment.getPropertySources()
.get("systemEnvironment");
OriginAwareSystemEnvironmentPropertySource replaced = (OriginAwareSystemEnvironmentPropertySource) this.environment
.getPropertySources().get("systemEnvironment");
Map<String, Object> originalMap = (Map<String, Object>) original.getSource();
Map<String, Object> replacedMap = replaced
.getSource();
Map<String, Object> replacedMap = replaced.getSource();
for (Map.Entry<String, Object> entry : originalMap.entrySet()) {
Object actual = replacedMap.get(entry.getKey());
assertThat(actual).isEqualTo(entry.getValue());
assertThat(replaced.getOrigin(entry.getKey())).isInstanceOf(SystemEnvironmentOrigin.class);
assertThat(replaced.getOrigin(entry.getKey()))
.isInstanceOf(SystemEnvironmentOrigin.class);
}
}
@Test
public void replacedPropertySourceWhenPropertyAbsentShouldReturnNullOrigin() throws Exception {
public void replacedPropertySourceWhenPropertyAbsentShouldReturnNullOrigin()
throws Exception {
SystemEnvironmentPropertySourceEnvironmentPostProcessor postProcessor = new SystemEnvironmentPropertySourceEnvironmentPostProcessor();
postProcessor.postProcessEnvironment(this.environment, null);
OriginAwareSystemEnvironmentPropertySource replaced = (OriginAwareSystemEnvironmentPropertySource) this.environment.getPropertySources()
.get("systemEnvironment");
OriginAwareSystemEnvironmentPropertySource replaced = (OriginAwareSystemEnvironmentPropertySource) this.environment
.getPropertySources().get("systemEnvironment");
assertThat(replaced.getOrigin("NON_EXISTENT")).isNull();
}
@Test
@SuppressWarnings("unchecked")
public void replacedPropertySourceShouldResolveProperty() throws Exception {
SystemEnvironmentPropertySourceEnvironmentPostProcessor postProcessor = new SystemEnvironmentPropertySourceEnvironmentPostProcessor();
Map<String, Object> source = Collections.singletonMap("FOO_BAR_BAZ", "hello");
this.environment.getPropertySources().replace("systemEnvironment", new SystemEnvironmentPropertySource("systemEnvironment", source));
this.environment.getPropertySources().replace("systemEnvironment",
new SystemEnvironmentPropertySource("systemEnvironment", source));
postProcessor.postProcessEnvironment(this.environment, null);
OriginAwareSystemEnvironmentPropertySource replaced = (OriginAwareSystemEnvironmentPropertySource) this.environment.getPropertySources()
.get("systemEnvironment");
SystemEnvironmentOrigin origin = (SystemEnvironmentOrigin) replaced.getOrigin("foo.bar.baz");
OriginAwareSystemEnvironmentPropertySource replaced = (OriginAwareSystemEnvironmentPropertySource) this.environment
.getPropertySources().get("systemEnvironment");
SystemEnvironmentOrigin origin = (SystemEnvironmentOrigin) replaced
.getOrigin("foo.bar.baz");
assertThat(origin.getProperty()).isEqualTo("FOO_BAR_BAZ");
assertThat(replaced.getProperty("foo.bar.baz")).isEqualTo("hello");
}

View File

@ -29,6 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link JsonComponentModule}.
*
* @author Phillip Webb
* @author Vladimir Tsanev
*/
public class JsonComponentModuleTests {
@ -73,11 +74,11 @@ public class JsonComponentModuleTests {
}
private void load(Class<?>... configs) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(configs);
ctx.register(JsonComponentModule.class);
ctx.refresh();
this.context = ctx;
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(configs);
context.register(JsonComponentModule.class);
context.refresh();
this.context = context;
}
private void assertSerialize(Module module) throws Exception {
@ -117,5 +118,7 @@ public class JsonComponentModuleTests {
static class ConcreteSerializer extends AbstractSerializer {
}
}
}

View File

@ -32,4 +32,5 @@ public class NettyWebServerTests {
this.server = new NettyWebServer(null, null);
this.server.stop();
}
}