Polish
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>() {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,4 +24,5 @@ import org.springframework.data.repository.CrudRepository;
|
||||
* @author Jayaram Pradhan
|
||||
*/
|
||||
public interface ExampleRepository extends CrudRepository<PersonHash, String> {
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()]))
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 == '-';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 + "'";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,4 +32,5 @@ public class NettyWebServerTests {
|
||||
this.server = new NettyWebServer(null, null);
|
||||
this.server.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user