From 38d78baa770a47211d88e24b4e3ee87624786e37 Mon Sep 17 00:00:00 2001 From: bono007 Date: Sat, 21 Aug 2021 09:32:16 -0500 Subject: [PATCH 1/7] Add support for redis-sentinel and redis-socket in spring.redis.url when using Lettuce. Fixes gh-21920 --- .../redis/LettuceConnectionConfiguration.java | 24 +- .../RedisAutoConfigurationJedisTests.java | 13 + ...RedisAutoConfigurationLettuceUrlTests.java | 280 ++++++++++++++++++ .../redis/RedisAutoConfigurationTests.java | 64 +--- 4 files changed, 315 insertions(+), 66 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java index 9d7f20cb3d73..c5fa422ffbd7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java @@ -20,6 +20,7 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.RedisClient; +import io.lettuce.core.RedisURI; import io.lettuce.core.SocketOptions; import io.lettuce.core.TimeoutOptions; import io.lettuce.core.cluster.ClusterClientOptions; @@ -38,6 +39,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; +import org.springframework.data.redis.connection.RedisConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisSentinelConfiguration; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; @@ -45,6 +47,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -84,6 +87,11 @@ LettuceConnectionFactory redisConnectionFactory( } private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) { + if (StringUtils.hasText(this.getProperties().getUrl())) { + RedisURI redisURI = createRedisUri(getProperties().getUrl()); + RedisConfiguration redisConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); + return new LettuceConnectionFactory(redisConfiguration, clientConfiguration); + } if (getSentinelConfig() != null) { return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration); } @@ -161,10 +169,20 @@ private ClientOptions.Builder initializeClientOptionsBuilder() { } private void customizeConfigurationFromUrl(LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) { - ConnectionInfo connectionInfo = parseUrl(getProperties().getUrl()); - if (connectionInfo.isUseSsl()) { - builder.useSsl(); + RedisURI redisURI = createRedisUri(this.getProperties().getUrl()); + builder.apply(redisURI); + } + + private RedisURI createRedisUri(String uri) { + RedisURI redisURI = RedisURI.create(uri); + // Set the sentinel password from properties on the sentinel nodes as there is no + // way to set that on the url + if (!ObjectUtils.isEmpty(redisURI.getSentinels()) && getProperties().getSentinel() != null + && StringUtils.hasText(getProperties().getSentinel().getPassword())) { + redisURI.getSentinels().forEach((sentinelNodeRedisUri) -> sentinelNodeRedisUri + .setPassword(getProperties().getSentinel().getPassword().toCharArray())); } + return redisURI; } /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java index 4a834b40bb47..421169ecf08e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java @@ -32,6 +32,7 @@ import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** * Tests for {@link RedisAutoConfiguration} when Lettuce is not on the classpath. @@ -39,6 +40,7 @@ * @author Mark Paluch * @author Stephane Nicoll * @author Weix Sun + * @author Chris Bono */ @ClassPathExclusions("lettuce-core-*.jar") class RedisAutoConfigurationJedisTests { @@ -209,6 +211,17 @@ void testRedisConfigurationWithSentinelAndAuthentication() { }); } + @Test + void testRedisSentinelUrlConfiguration() { + this.contextRunner + .withPropertyValues( + "spring.redis.url=redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/mymaster") + .run((context) -> assertThatIllegalStateException() + .isThrownBy(() -> context.getBean(JedisConnectionFactory.class)) + .withRootCauseInstanceOf(RedisUrlSyntaxException.class).havingRootCause().withMessageContaining( + "Invalid Redis URL 'redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/mymaster'")); + } + @Test void testRedisConfigurationWithCluster() { this.contextRunner.withPropertyValues("spring.redis.cluster.nodes=127.0.0.1:27379,127.0.0.1:27380") diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java new file mode 100644 index 000000000000..40c2375550cf --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java @@ -0,0 +1,280 @@ +/* + * Copyright 2012-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.data.redis; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.data.redis.connection.RedisSentinelConfiguration; +import org.springframework.data.redis.connection.RedisSocketConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests {@link RedisAutoConfiguration} when using {@code Lettuce} and configuring via the + * {@code spring.redis.url} property. + * + * @author Chris Bono + */ +class RedisAutoConfigurationLettuceUrlTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)); + + @Test + void redisStandaloneUrlWithMinimalFields() { + this.contextRunner.withPropertyValues("spring.redis.url=redis://host1").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(6379); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isNullOrEmpty(); + assertThat(cf.isUseSsl()).isFalse(); + }); + } + + @Test + void redisStandaloneUrlWithAllFields() { + this.contextRunner.withPropertyValues("spring.redis.url=redis://user:password@host1:33/7").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isFalse(); + }); + } + + @Test + void redisStandaloneUrlIgnoresOtherProps() { + this.contextRunner.withPropertyValues("spring.redis.url=redis://user:password@host1:33/7", + "spring.redis.username=abc", "spring.redis.password=xyz", "spring.redis.host=foo", + "spring.redis.port=1000", "spring.redis.database=2", "spring.redis.ssl=true").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isFalse(); + }); + } + + @Test + void redisStandaloneWithSslUrl() { + this.contextRunner.withPropertyValues("spring.redis.url=rediss://user:password@host1:33/7").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isTrue(); + }); + } + + @Test + void redisStandaloneWithAltSslUrl() { + this.contextRunner.withPropertyValues("spring.redis.url=redis+ssl://user:password@host1:33/7") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isTrue(); + }); + } + + @Test + void redisStandaloneWithAltTlsUrl() { + this.contextRunner.withPropertyValues("spring.redis.url=redis+tls://user:password@host1:33/7") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isTrue(); + assertThat(cf.isStartTls()).isTrue(); + }); + } + + @Test + void redisStandaloneUrlWithoutUsernameWithPasswordContainingColon() { + this.contextRunner.withPropertyValues("spring.redis.url=redis://:pass:word@host1:33").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isEqualTo("pass:word"); + }); + } + + @Test + void redisStandaloneUrlWithUsernameWithPasswordStartsWithColonAndContainsColon() { + this.contextRunner.withPropertyValues("spring.redis.url=redis://user::pass:word@host1:33").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo(":pass:word"); + }); + } + + @Test + void redisSentinelUrlMinimalFields() { + this.contextRunner.withPropertyValues("spring.redis.url=redis-sentinel://127.0.0.1?sentinelMasterId=5150") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isNullOrEmpty(); + assertThat(cf.getDatabase()).isEqualTo(0); + assertThat(cf.isUseSsl()).isFalse(); + assertThat(cf.isRedisSentinelAware()).isTrue(); + RedisSentinelConfiguration sentinelConfiguration = cf.getSentinelConfiguration(); + assertThat(sentinelConfiguration.getSentinels()).flatMap(Object::toString) + .containsExactlyInAnyOrder("127.0.0.1:26379"); + assertThat(sentinelConfiguration.getMaster().getName()).isEqualTo("5150"); + assertThat(sentinelConfiguration.getSentinelPassword().isPresent()).isFalse(); + }); + } + + @Test + void redisSentinelUrlWithAllFields() { + this.contextRunner.withPropertyValues( + "spring.redis.url=redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/7?sentinelMasterId=5150", + "spring.redis.sentinel.password: secret").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isEqualTo("username"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isFalse(); + assertThat(cf.isRedisSentinelAware()).isTrue(); + RedisSentinelConfiguration sentinelConfiguration = cf.getSentinelConfiguration(); + assertThat(sentinelConfiguration.getSentinels()).flatMap(Object::toString) + .containsExactlyInAnyOrder("127.0.0.1:26379", "127.0.0.1:26380"); + assertThat(sentinelConfiguration.getMaster().getName()).isEqualTo("5150"); + assertThat(sentinelConfiguration.getSentinelPassword().get()).isEqualTo("secret".toCharArray()); + }); + + } + + @Test + void redisSentinelUrlIgnoresOtherProps() { + this.contextRunner.withPropertyValues( + "spring.redis.url=redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/7?sentinelMasterId=5150", + "spring.redis.sentinel.master=mymaster", "spring.redis.sentinel.nodes=server1:111, server2:222") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isEqualTo("username"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isFalse(); + assertThat(cf.isRedisSentinelAware()).isTrue(); + RedisSentinelConfiguration sentinelConfiguration = cf.getSentinelConfiguration(); + assertThat(sentinelConfiguration.getSentinels()).flatMap(Object::toString) + .containsExactlyInAnyOrder("127.0.0.1:26379", "127.0.0.1:26380"); + assertThat(sentinelConfiguration.getMaster().getName()).isEqualTo("5150"); + }); + } + + @Test + void redisSentinelWithSslUrl() { + this.contextRunner.withPropertyValues("spring.redis.url=rediss-sentinel://127.0.0.1?sentinelMasterId=5150") + .run((context) -> { + LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class); + assertThat(connectionFactory.isRedisSentinelAware()).isTrue(); + assertThat(connectionFactory.isUseSsl()).isTrue(); + }); + } + + @Test + void redisSocketUrlWithMinimalFields() { + this.contextRunner.withPropertyValues("spring.redis.url=redis-socket:///mysocket").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isNullOrEmpty(); + assertThat(cf.getDatabase()).isEqualTo(0); + assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) + .isEqualTo("/mysocket"); + }); + } + + @Test + void redisSocketUrlWithAllFields() { + this.contextRunner.withPropertyValues("spring.redis.url=redis-socket://user:password@/mysocket?database=7") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) + .isEqualTo("/mysocket"); + }); + } + + @Test + void redisSocketUrlWithAltScheme() { + this.contextRunner.withPropertyValues("spring.redis.url=redis+socket://user:password@/mysocket?database=7") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) + .isEqualTo("/mysocket"); + }); + } + + @Test + void redisUrlWithCientOptionParamsOverridesAndRespectsOtherProps() { + this.contextRunner.withPropertyValues("spring.redis.url=redis://host1?timeout=47s&clientName=zuser", + "spring.redis.timeout=1200", "spring.redis.connect-timeout=2400", + "spring.redis.lettuce.shutdown-timeout=3600").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getClientName()).isEqualTo("zuser"); + assertThat(cf.getTimeout()).isEqualTo(47000); + assertThat(cf.getShutdownTimeout()).isEqualTo(3600); + assertThat(cf.getClientConfiguration().getClientOptions().get().getSocketOptions() + .getConnectTimeout().toMillis()).isEqualTo(2400); + }); + } + + @Test + void redisUrlWithDefaultCientOptions() { + this.contextRunner.withPropertyValues("spring.redis.url=redis://host1").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getClientName()).isNullOrEmpty(); + assertThat(cf.getTimeout()).isEqualTo(60000); + assertThat(cf.getShutdownTimeout()).isEqualTo(100); + assertThat(cf.getClientConfiguration().getClientOptions().get().getSocketOptions().getConnectTimeout() + .toMillis()).isEqualTo(10000); + }); + } + + private String getUserName(LettuceConnectionFactory factory) { + return ReflectionTestUtils.invokeMethod(factory, "getRedisUsername"); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java index cbc08550358f..0d36a4aa970d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java @@ -56,7 +56,6 @@ import org.springframework.util.StringUtils; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.Mockito.mock; /** @@ -72,6 +71,7 @@ * @author Alen Turkovic * @author Scott Frederick * @author Weix Sun + * @author Chris Bono */ class RedisAutoConfigurationTests { @@ -120,57 +120,6 @@ void testCustomizeRedisConfiguration() { }); } - @Test - void testRedisUrlConfiguration() { - this.contextRunner - .withPropertyValues("spring.redis.host:foo", "spring.redis.url:redis://user:password@example:33") - .run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.isUseSsl()).isFalse(); - }); - } - - @Test - void testOverrideUrlRedisConfiguration() { - this.contextRunner - .withPropertyValues("spring.redis.host:foo", "spring.redis.password:xyz", "spring.redis.port:1000", - "spring.redis.ssl:false", "spring.redis.url:rediss://user:password@example:33") - .run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.isUseSsl()).isTrue(); - }); - } - - @Test - void testPasswordInUrlWithColon() { - this.contextRunner.withPropertyValues("spring.redis.url:redis://:pass:word@example:33").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo(""); - assertThat(cf.getPassword()).isEqualTo("pass:word"); - }); - } - - @Test - void testPasswordInUrlStartsWithColon() { - this.contextRunner.withPropertyValues("spring.redis.url:redis://user::pass:word@example:33").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo(":pass:word"); - }); - } - @Test void testRedisConfigurationUsePoolByDefault() { Pool defaultPool = new RedisProperties().getLettuce().getPool(); @@ -322,17 +271,6 @@ void testRedisConfigurationWithSentinelPasswordAndDataNodePassword() { }); } - @Test - void testRedisSentinelUrlConfiguration() { - this.contextRunner - .withPropertyValues( - "spring.redis.url=redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/mymaster") - .run((context) -> assertThatIllegalStateException() - .isThrownBy(() -> context.getBean(LettuceConnectionFactory.class)) - .withRootCauseInstanceOf(RedisUrlSyntaxException.class).havingRootCause().withMessageContaining( - "Invalid Redis URL 'redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/mymaster'")); - } - @Test void testRedisConfigurationWithCluster() { List clusterNodes = Arrays.asList("127.0.0.1:27379", "127.0.0.1:27380"); From 781902e9fe2c54e8b72b91362e7faeb18763b8a2 Mon Sep 17 00:00:00 2001 From: bono007 Date: Sun, 22 Aug 2021 09:44:54 -0500 Subject: [PATCH 2/7] Code review feedback: removed unnecessary "this." --- .../data/redis/LettuceConnectionConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java index c5fa422ffbd7..30673764c133 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java @@ -169,7 +169,7 @@ private ClientOptions.Builder initializeClientOptionsBuilder() { } private void customizeConfigurationFromUrl(LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) { - RedisURI redisURI = createRedisUri(this.getProperties().getUrl()); + RedisURI redisURI = createRedisUri(getProperties().getUrl()); builder.apply(redisURI); } From 9b0a9413f32694d2c38ac68b1a964992df1be443 Mon Sep 17 00:00:00 2001 From: bono007 Date: Sat, 21 Aug 2021 10:14:33 -0500 Subject: [PATCH 3/7] Add spring.data.redis.url configuration info to nosql.adoc --- .../src/docs/asciidoc/data/nosql.adoc | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc index 18982ebb689c..b56f42c5db12 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc @@ -41,6 +41,41 @@ The following listing shows an example of such a bean: include::{docs-java}/data/nosql/redis/connecting/MyBean.java[] ---- +You can set the configprop:spring.data.redis.url[] property to change the URL and configure +additional settings as shown in the following example: + +[source,yaml,indent=0,subs="verbatim",configprops,configblocks] +---- + spring: + data: + redis: + url: "redis://user:secret@redis.example.com:6399 +---- + +Alternatively, you can specify connection details using discrete properties. For example, you might +declare the following settings in your `application.yml`: + +[source,yaml,indent=0,subs="verbatim",configprops,configblocks] +---- + spring: + data: + redis: + host: "redis.example.com" + port: 6399 + username: "user" + password: "secret" +---- +When using both the URL and the discrete properties, any values that are set in the URL will take precedence over the values set in the properties. + +The url property supports the `redis` and `rediss` schemes as well as the following additional schemes when using Lettuce: + +* `redis+ssl` +* `redis+tls` +* `redis-sentinel` +* `rediss-sentinel` +* `redis-socket` +* `redis+socket` + TIP: You can also register an arbitrary number of beans that implement `LettuceClientConfigurationBuilderCustomizer` for more advanced customizations. `ClientResources` can also be customized using `ClientResourcesBuilderCustomizer`. If you use Jedis, `JedisClientConfigurationBuilderCustomizer` is also available. From 3137566a081f0f8531c8d7c5ccaccb09d87049b4 Mon Sep 17 00:00:00 2001 From: bono007 Date: Sat, 9 Oct 2021 14:57:57 -0500 Subject: [PATCH 4/7] Only use the new RedisURI approach when the user has specified a non-standard standalone scheme OR redis-sentinel scheme --- .../redis/LettuceConnectionConfiguration.java | 48 ++++++++++++++++--- .../redis/RedisConnectionConfiguration.java | 20 ++++++++ 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java index 30673764c133..a88655d6dbc7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.data.redis; +import java.net.URI; +import java.net.URISyntaxException; import java.time.Duration; import io.lettuce.core.ClientOptions; @@ -39,7 +41,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; -import org.springframework.data.redis.connection.RedisConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisSentinelConfiguration; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; @@ -87,11 +88,6 @@ LettuceConnectionFactory redisConnectionFactory( } private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) { - if (StringUtils.hasText(this.getProperties().getUrl())) { - RedisURI redisURI = createRedisUri(getProperties().getUrl()); - RedisConfiguration redisConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); - return new LettuceConnectionFactory(redisConfiguration, clientConfiguration); - } if (getSentinelConfig() != null) { return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration); } @@ -101,6 +97,46 @@ private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientCon return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration); } + @Override + RedisSentinelConfiguration maybeGetCustomSentinelConfig() { + if (!urlConfiguredForSentinelScheme(this.getProperties().getUrl())) { + return null; + } + RedisURI redisURI = createRedisUri(this.getProperties().getUrl()); + return (RedisSentinelConfiguration) LettuceConnectionFactory.createRedisConfiguration(redisURI); + } + + @Override + RedisStandaloneConfiguration maybeGetCustomStandaloneConfig() { + if (!urlConfiguredForNonStandardStandaloneScheme(this.getProperties().getUrl())) { + return null; + } + RedisURI redisURI = createRedisUri(this.getProperties().getUrl()); + return (RedisStandaloneConfiguration) LettuceConnectionFactory.createRedisConfiguration(redisURI); + } + + private boolean urlConfiguredForSentinelScheme(String url) { + String scheme = getUrlScheme(url); + return "redis-sentinel".equals(scheme) || "rediss-sentinel".equals(scheme); + } + + private boolean urlConfiguredForNonStandardStandaloneScheme(String url) { + String scheme = getUrlScheme(url); + return !"redis".equals(scheme) && !"rediss".equals(scheme) && !urlConfiguredForSentinelScheme(url); + } + + private String getUrlScheme(String url) { + if (!StringUtils.hasText(url)) { + return null; + } + try { + URI uri = new URI(url); + return uri.getScheme(); + } catch (URISyntaxException ex) { + throw new RedisUrlSyntaxException(url, ex); + } + } + private LettuceClientConfiguration getLettuceClientConfiguration( ObjectProvider builderCustomizers, ClientResources clientResources, Pool pool) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java index b870e4a5508b..4c134a87af10 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java @@ -68,6 +68,12 @@ protected final RedisStandaloneConfiguration getStandaloneConfig() { if (this.standaloneConfiguration != null) { return this.standaloneConfiguration; } + + RedisStandaloneConfiguration customConfig = maybeGetCustomStandaloneConfig(); + if (customConfig != null) { + return customConfig; + } + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); if (StringUtils.hasText(this.properties.getUrl())) { ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl()); @@ -86,10 +92,20 @@ protected final RedisStandaloneConfiguration getStandaloneConfig() { return config; } + RedisStandaloneConfiguration maybeGetCustomStandaloneConfig() { + return null; + } + protected final RedisSentinelConfiguration getSentinelConfig() { if (this.sentinelConfiguration != null) { return this.sentinelConfiguration; } + + RedisSentinelConfiguration customConfig = maybeGetCustomSentinelConfig(); + if (customConfig != null) { + return customConfig; + } + RedisProperties.Sentinel sentinelProperties = this.properties.getSentinel(); if (sentinelProperties != null) { RedisSentinelConfiguration config = new RedisSentinelConfiguration(); @@ -108,6 +124,10 @@ protected final RedisSentinelConfiguration getSentinelConfig() { return null; } + RedisSentinelConfiguration maybeGetCustomSentinelConfig() { + return null; + } + /** * Create a {@link RedisClusterConfiguration} if necessary. * @return {@literal null} if no cluster settings are set. From 7c17cece5de2a1b277248ee60ccb103f812ad88d Mon Sep 17 00:00:00 2001 From: bono007 Date: Mon, 18 Oct 2021 17:55:34 -0500 Subject: [PATCH 5/7] Updated LettuceConnectionConfiguration for explicit support of RedisSocketConfiguration. Also updated test. --- .../redis/LettuceConnectionConfiguration.java | 48 +- ...RedisAutoConfigurationLettuceUrlTests.java | 450 +++++++++--------- 2 files changed, 272 insertions(+), 226 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java index a88655d6dbc7..c785c74ef77d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java @@ -43,6 +43,7 @@ import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisSentinelConfiguration; +import org.springframework.data.redis.connection.RedisSocketConfiguration; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder; @@ -62,11 +63,15 @@ @ConditionalOnProperty(name = "spring.redis.client-type", havingValue = "lettuce", matchIfMissing = true) class LettuceConnectionConfiguration extends RedisConnectionConfiguration { + private final RedisSocketConfiguration socketConfiguration; + LettuceConnectionConfiguration(RedisProperties properties, ObjectProvider standaloneConfigurationProvider, + ObjectProvider socketConfigurationProvider, ObjectProvider sentinelConfigurationProvider, ObjectProvider clusterConfigurationProvider) { super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider); + this.socketConfiguration = socketConfigurationProvider.getIfAvailable(); } @Bean(destroyMethod = "shutdown") @@ -94,35 +99,55 @@ private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientCon if (getClusterConfiguration() != null) { return new LettuceConnectionFactory(getClusterConfiguration(), clientConfiguration); } + if (getSocketConfiguration() != null) { + return new LettuceConnectionFactory(getSocketConfiguration(), clientConfiguration); + } return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration); } @Override RedisSentinelConfiguration maybeGetCustomSentinelConfig() { - if (!urlConfiguredForSentinelScheme(this.getProperties().getUrl())) { - return null; + if (urlConfiguredForSentinelScheme(this.getProperties().getUrl())) { + RedisURI redisURI = createRedisUri(this.getProperties().getUrl()); + return (RedisSentinelConfiguration) LettuceConnectionFactory.createRedisConfiguration(redisURI); } - RedisURI redisURI = createRedisUri(this.getProperties().getUrl()); - return (RedisSentinelConfiguration) LettuceConnectionFactory.createRedisConfiguration(redisURI); + return null; } @Override RedisStandaloneConfiguration maybeGetCustomStandaloneConfig() { - if (!urlConfiguredForNonStandardStandaloneScheme(this.getProperties().getUrl())) { - return null; + if (urlConfiguredForNonStandardStandaloneScheme(this.getProperties().getUrl())) { + RedisURI redisURI = createRedisUri(this.getProperties().getUrl()); + return (RedisStandaloneConfiguration) LettuceConnectionFactory.createRedisConfiguration(redisURI); + } + return null; + } + + private RedisSocketConfiguration getSocketConfiguration() { + if (this.socketConfiguration != null) { + return this.socketConfiguration; + } + if (urlConfiguredForSocketScheme(this.getProperties().getUrl())) { + RedisURI redisURI = createRedisUri(this.getProperties().getUrl()); + return (RedisSocketConfiguration) LettuceConnectionFactory.createRedisConfiguration(redisURI); } - RedisURI redisURI = createRedisUri(this.getProperties().getUrl()); - return (RedisStandaloneConfiguration) LettuceConnectionFactory.createRedisConfiguration(redisURI); + return null; } private boolean urlConfiguredForSentinelScheme(String url) { String scheme = getUrlScheme(url); - return "redis-sentinel".equals(scheme) || "rediss-sentinel".equals(scheme); + return scheme != null + && RedisURI.URI_SCHEME_REDIS_SENTINEL.equals(scheme) || RedisURI.URI_SCHEME_REDIS_SENTINEL_SECURE.equals(scheme); + } + + private boolean urlConfiguredForSocketScheme(String url) { + String scheme = getUrlScheme(url); + return scheme != null && (RedisURI.URI_SCHEME_REDIS_SOCKET.equals(scheme) || RedisURI.URI_SCHEME_REDIS_SOCKET_ALT.equals(scheme)); } private boolean urlConfiguredForNonStandardStandaloneScheme(String url) { String scheme = getUrlScheme(url); - return !"redis".equals(scheme) && !"rediss".equals(scheme) && !urlConfiguredForSentinelScheme(url); + return scheme != null && !RedisURI.URI_SCHEME_REDIS.equals(scheme) && !RedisURI.URI_SCHEME_REDIS_SECURE.equals(scheme); } private String getUrlScheme(String url) { @@ -132,7 +157,8 @@ private String getUrlScheme(String url) { try { URI uri = new URI(url); return uri.getScheme(); - } catch (URISyntaxException ex) { + } + catch (URISyntaxException ex) { throw new RedisUrlSyntaxException(url, ex); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java index 40c2375550cf..0365a0fa7747 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.data.redis; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -38,243 +39,262 @@ class RedisAutoConfigurationLettuceUrlTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)); - @Test - void redisStandaloneUrlWithMinimalFields() { - this.contextRunner.withPropertyValues("spring.redis.url=redis://host1").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(6379); - assertThat(getUserName(cf)).isNullOrEmpty(); - assertThat(cf.getPassword()).isNullOrEmpty(); - assertThat(cf.isUseSsl()).isFalse(); - }); - } - @Test - void redisStandaloneUrlWithAllFields() { - this.contextRunner.withPropertyValues("spring.redis.url=redis://user:password@host1:33/7").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.isUseSsl()).isFalse(); - }); + private String getUserName(LettuceConnectionFactory factory) { + return ReflectionTestUtils.invokeMethod(factory, "getRedisUsername"); } - @Test - void redisStandaloneUrlIgnoresOtherProps() { - this.contextRunner.withPropertyValues("spring.redis.url=redis://user:password@host1:33/7", - "spring.redis.username=abc", "spring.redis.password=xyz", "spring.redis.host=foo", - "spring.redis.port=1000", "spring.redis.database=2", "spring.redis.ssl=true").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.isUseSsl()).isFalse(); - }); - } + @Nested + class RedisStandaloneUrlWithStandardScheme { - @Test - void redisStandaloneWithSslUrl() { - this.contextRunner.withPropertyValues("spring.redis.url=rediss://user:password@host1:33/7").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.isUseSsl()).isTrue(); - }); - } + @Test + void withMinimalFields() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis://host1:6379").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(6379); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isNullOrEmpty(); + assertThat(cf.isUseSsl()).isFalse(); + }); + } - @Test - void redisStandaloneWithAltSslUrl() { - this.contextRunner.withPropertyValues("spring.redis.url=redis+ssl://user:password@host1:33/7") - .run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.isUseSsl()).isTrue(); - }); - } + @Test + void withAllFields() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis://user:password@host1:33").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.isUseSsl()).isFalse(); + }); + } - @Test - void redisStandaloneWithAltTlsUrl() { - this.contextRunner.withPropertyValues("spring.redis.url=redis+tls://user:password@host1:33/7") - .run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.isUseSsl()).isTrue(); - assertThat(cf.isStartTls()).isTrue(); - }); - } + @Test + void withSsl() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=rediss://user:password@host1:33").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.isUseSsl()).isTrue(); + }); + } - @Test - void redisStandaloneUrlWithoutUsernameWithPasswordContainingColon() { - this.contextRunner.withPropertyValues("spring.redis.url=redis://:pass:word@host1:33").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isNullOrEmpty(); - assertThat(cf.getPassword()).isEqualTo("pass:word"); - }); - } + @Test + void withoutUsernameWithPasswordContainingColon() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis://:pass:word@host1:33").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isEqualTo("pass:word"); + }); + } - @Test - void redisStandaloneUrlWithUsernameWithPasswordStartsWithColonAndContainsColon() { - this.contextRunner.withPropertyValues("spring.redis.url=redis://user::pass:word@host1:33").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo(":pass:word"); - }); - } + @Test + void withUsernameWithPasswordStartsWithColonAndContainsColon() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis://user::pass:word@host1:33").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo(":pass:word"); + }); + } - @Test - void redisSentinelUrlMinimalFields() { - this.contextRunner.withPropertyValues("spring.redis.url=redis-sentinel://127.0.0.1?sentinelMasterId=5150") - .run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(getUserName(cf)).isNullOrEmpty(); - assertThat(cf.getPassword()).isNullOrEmpty(); - assertThat(cf.getDatabase()).isEqualTo(0); - assertThat(cf.isUseSsl()).isFalse(); - assertThat(cf.isRedisSentinelAware()).isTrue(); - RedisSentinelConfiguration sentinelConfiguration = cf.getSentinelConfiguration(); - assertThat(sentinelConfiguration.getSentinels()).flatMap(Object::toString) - .containsExactlyInAnyOrder("127.0.0.1:26379"); - assertThat(sentinelConfiguration.getMaster().getName()).isEqualTo("5150"); - assertThat(sentinelConfiguration.getSentinelPassword().isPresent()).isFalse(); - }); } - @Test - void redisSentinelUrlWithAllFields() { - this.contextRunner.withPropertyValues( - "spring.redis.url=redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/7?sentinelMasterId=5150", - "spring.redis.sentinel.password: secret").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(getUserName(cf)).isEqualTo("username"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.isUseSsl()).isFalse(); - assertThat(cf.isRedisSentinelAware()).isTrue(); - RedisSentinelConfiguration sentinelConfiguration = cf.getSentinelConfiguration(); - assertThat(sentinelConfiguration.getSentinels()).flatMap(Object::toString) - .containsExactlyInAnyOrder("127.0.0.1:26379", "127.0.0.1:26380"); - assertThat(sentinelConfiguration.getMaster().getName()).isEqualTo("5150"); - assertThat(sentinelConfiguration.getSentinelPassword().get()).isEqualTo("secret".toCharArray()); - }); + @Nested + class RedisStandaloneUrlWithNonStandardScheme { - } + @Test + void withAltSsl() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis+ssl://user:password@host1:33/7").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isTrue(); + }); + } - @Test - void redisSentinelUrlIgnoresOtherProps() { - this.contextRunner.withPropertyValues( - "spring.redis.url=redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/7?sentinelMasterId=5150", - "spring.redis.sentinel.master=mymaster", "spring.redis.sentinel.nodes=server1:111, server2:222") - .run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(getUserName(cf)).isEqualTo("username"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.isUseSsl()).isFalse(); - assertThat(cf.isRedisSentinelAware()).isTrue(); - RedisSentinelConfiguration sentinelConfiguration = cf.getSentinelConfiguration(); - assertThat(sentinelConfiguration.getSentinels()).flatMap(Object::toString) - .containsExactlyInAnyOrder("127.0.0.1:26379", "127.0.0.1:26380"); - assertThat(sentinelConfiguration.getMaster().getName()).isEqualTo("5150"); - }); - } + @Test + void withAltTls() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis+tls://user:password@host1:33/7").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isTrue(); + assertThat(cf.isStartTls()).isTrue(); + }); + } - @Test - void redisSentinelWithSslUrl() { - this.contextRunner.withPropertyValues("spring.redis.url=rediss-sentinel://127.0.0.1?sentinelMasterId=5150") - .run((context) -> { - LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class); - assertThat(connectionFactory.isRedisSentinelAware()).isTrue(); - assertThat(connectionFactory.isUseSsl()).isTrue(); - }); - } + @Test + void withPropsSetOnUrlIgnoresPropsSetInConfig() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis+ssl://user:password@host1:33/7", + "spring.redis.username=abc", "spring.redis.password=xyz", "spring.redis.host=foo", + "spring.redis.port=1000", "spring.redis.database=2", "spring.redis.ssl=false") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isTrue(); + }); + } - @Test - void redisSocketUrlWithMinimalFields() { - this.contextRunner.withPropertyValues("spring.redis.url=redis-socket:///mysocket").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(getUserName(cf)).isNullOrEmpty(); - assertThat(cf.getPassword()).isNullOrEmpty(); - assertThat(cf.getDatabase()).isEqualTo(0); - assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) - .isEqualTo("/mysocket"); - }); - } + @Test + void withClientOptionPropsSetOnUrlOverridesAndRespectsPropsSetInConfig() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis+ssl://host1?timeout=47s&clientName=zuser", + "spring.redis.timeout=1200", "spring.redis.connect-timeout=2400", + "spring.redis.lettuce.shutdown-timeout=3600").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getClientName()).isEqualTo("zuser"); + assertThat(cf.getTimeout()).isEqualTo(47000); + assertThat(cf.getShutdownTimeout()).isEqualTo(3600); + assertThat(cf.getClientConfiguration().getClientOptions().get().getSocketOptions() + .getConnectTimeout().toMillis()).isEqualTo(2400); + }); + } - @Test - void redisSocketUrlWithAllFields() { - this.contextRunner.withPropertyValues("spring.redis.url=redis-socket://user:password@/mysocket?database=7") - .run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) - .isEqualTo("/mysocket"); - }); - } + @Test + void withoutClientOptionPropsSetOnUrlUsesDefaultCientOptions() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis+ssl://host1").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getClientName()).isNullOrEmpty(); + assertThat(cf.getTimeout()).isEqualTo(60000); + assertThat(cf.getShutdownTimeout()).isEqualTo(100); + assertThat(cf.getClientConfiguration().getClientOptions().get().getSocketOptions().getConnectTimeout() + .toMillis()).isEqualTo(10000); + }); + } - @Test - void redisSocketUrlWithAltScheme() { - this.contextRunner.withPropertyValues("spring.redis.url=redis+socket://user:password@/mysocket?database=7") - .run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) - .isEqualTo("/mysocket"); - }); } - @Test - void redisUrlWithCientOptionParamsOverridesAndRespectsOtherProps() { - this.contextRunner.withPropertyValues("spring.redis.url=redis://host1?timeout=47s&clientName=zuser", - "spring.redis.timeout=1200", "spring.redis.connect-timeout=2400", - "spring.redis.lettuce.shutdown-timeout=3600").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getClientName()).isEqualTo("zuser"); - assertThat(cf.getTimeout()).isEqualTo(47000); - assertThat(cf.getShutdownTimeout()).isEqualTo(3600); - assertThat(cf.getClientConfiguration().getClientOptions().get().getSocketOptions() - .getConnectTimeout().toMillis()).isEqualTo(2400); - }); - } + @Nested + class RedisSentinelUrl { + + @Test + void withMinimalFields() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis-sentinel://127.0.0.1?sentinelMasterId=5150") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isNullOrEmpty(); + assertThat(cf.getDatabase()).isEqualTo(0); + assertThat(cf.isUseSsl()).isFalse(); + assertThat(cf.isRedisSentinelAware()).isTrue(); + RedisSentinelConfiguration sentinelConfiguration = cf.getSentinelConfiguration(); + assertThat(sentinelConfiguration.getSentinels()).flatMap(Object::toString) + .containsExactlyInAnyOrder("127.0.0.1:26379"); + assertThat(sentinelConfiguration.getMaster().getName()).isEqualTo("5150"); + assertThat(sentinelConfiguration.getSentinelPassword().isPresent()).isFalse(); + }); + } + + @Test + void withAllFields() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues( + "spring.redis.url=redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/7?sentinelMasterId=5150", + "spring.redis.sentinel.password: secret").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isEqualTo("username"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isFalse(); + assertThat(cf.isRedisSentinelAware()).isTrue(); + RedisSentinelConfiguration sentinelConfiguration = cf.getSentinelConfiguration(); + assertThat(sentinelConfiguration.getSentinels()).flatMap(Object::toString) + .containsExactlyInAnyOrder("127.0.0.1:26379", "127.0.0.1:26380"); + assertThat(sentinelConfiguration.getMaster().getName()).isEqualTo("5150"); + assertThat(sentinelConfiguration.getSentinelPassword().get()).isEqualTo("secret".toCharArray()); + }); + + } + + @Test + void withSsl() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=rediss-sentinel://127.0.0.1?sentinelMasterId=5150") + .run((context) -> { + LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class); + assertThat(connectionFactory.isRedisSentinelAware()).isTrue(); + assertThat(connectionFactory.isUseSsl()).isTrue(); + }); + } + + @Test + void withPropsSetOnUrlIgnoresPropsSetInConfig() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues( + "spring.redis.url=redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/7?sentinelMasterId=5150", + "spring.redis.sentinel.master=mymaster", "spring.redis.sentinel.nodes=server1:111, server2:222") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isEqualTo("username"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isFalse(); + assertThat(cf.isRedisSentinelAware()).isTrue(); + RedisSentinelConfiguration sentinelConfiguration = cf.getSentinelConfiguration(); + assertThat(sentinelConfiguration.getSentinels()).flatMap(Object::toString) + .containsExactlyInAnyOrder("127.0.0.1:26379", "127.0.0.1:26380"); + assertThat(sentinelConfiguration.getMaster().getName()).isEqualTo("5150"); + }); + } - @Test - void redisUrlWithDefaultCientOptions() { - this.contextRunner.withPropertyValues("spring.redis.url=redis://host1").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getClientName()).isNullOrEmpty(); - assertThat(cf.getTimeout()).isEqualTo(60000); - assertThat(cf.getShutdownTimeout()).isEqualTo(100); - assertThat(cf.getClientConfiguration().getClientOptions().get().getSocketOptions().getConnectTimeout() - .toMillis()).isEqualTo(10000); - }); } - private String getUserName(LettuceConnectionFactory factory) { - return ReflectionTestUtils.invokeMethod(factory, "getRedisUsername"); + @Nested + class RedisSocketUrl { + + @Test + void withMinimalFields() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis-socket:///mysocket").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isNullOrEmpty(); + assertThat(cf.getDatabase()).isEqualTo(0); + assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) + .isEqualTo("/mysocket"); + }); + } + + @Test + void withAllFields() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis-socket://user:password@/mysocket?database=7") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) + .isEqualTo("/mysocket"); + }); + } + + @Test + void withAltScheme() { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis+socket://user:password@/mysocket?database=7") + .run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) + .isEqualTo("/mysocket"); + }); + } + } } From a6be564990740eca2680a4d8d23f7b82fb6c39e7 Mon Sep 17 00:00:00 2001 From: bono007 Date: Tue, 19 Oct 2021 08:42:17 -0500 Subject: [PATCH 6/7] Fixed checkstyle and missing quote on asciidoc --- .../redis/LettuceConnectionConfiguration.java | 10 +- ...RedisAutoConfigurationLettuceUrlTests.java | 176 ++++++++++-------- .../src/docs/asciidoc/data/nosql.adoc | 4 +- 3 files changed, 103 insertions(+), 87 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java index c785c74ef77d..02cb7fc65f85 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java @@ -136,18 +136,20 @@ private RedisSocketConfiguration getSocketConfiguration() { private boolean urlConfiguredForSentinelScheme(String url) { String scheme = getUrlScheme(url); - return scheme != null - && RedisURI.URI_SCHEME_REDIS_SENTINEL.equals(scheme) || RedisURI.URI_SCHEME_REDIS_SENTINEL_SECURE.equals(scheme); + return scheme != null && RedisURI.URI_SCHEME_REDIS_SENTINEL.equals(scheme) + || RedisURI.URI_SCHEME_REDIS_SENTINEL_SECURE.equals(scheme); } private boolean urlConfiguredForSocketScheme(String url) { String scheme = getUrlScheme(url); - return scheme != null && (RedisURI.URI_SCHEME_REDIS_SOCKET.equals(scheme) || RedisURI.URI_SCHEME_REDIS_SOCKET_ALT.equals(scheme)); + return scheme != null && (RedisURI.URI_SCHEME_REDIS_SOCKET.equals(scheme) + || RedisURI.URI_SCHEME_REDIS_SOCKET_ALT.equals(scheme)); } private boolean urlConfiguredForNonStandardStandaloneScheme(String url) { String scheme = getUrlScheme(url); - return scheme != null && !RedisURI.URI_SCHEME_REDIS.equals(scheme) && !RedisURI.URI_SCHEME_REDIS_SECURE.equals(scheme); + return scheme != null && !RedisURI.URI_SCHEME_REDIS.equals(scheme) + && !RedisURI.URI_SCHEME_REDIS_SECURE.equals(scheme); } private String getUrlScheme(String url) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java index 0365a0fa7747..914d0af381e3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationLettuceUrlTests.java @@ -39,7 +39,6 @@ class RedisAutoConfigurationLettuceUrlTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)); - private String getUserName(LettuceConnectionFactory factory) { return ReflectionTestUtils.invokeMethod(factory, "getRedisUsername"); } @@ -49,60 +48,65 @@ class RedisStandaloneUrlWithStandardScheme { @Test void withMinimalFields() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis://host1:6379").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(6379); - assertThat(getUserName(cf)).isNullOrEmpty(); - assertThat(cf.getPassword()).isNullOrEmpty(); - assertThat(cf.isUseSsl()).isFalse(); - }); + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis://host1:6379").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(6379); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isNullOrEmpty(); + assertThat(cf.isUseSsl()).isFalse(); + }); } @Test void withAllFields() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis://user:password@host1:33").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.isUseSsl()).isFalse(); - }); + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis://user:password@host1:33").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.isUseSsl()).isFalse(); + }); } @Test void withSsl() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=rediss://user:password@host1:33").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.isUseSsl()).isTrue(); - }); + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=rediss://user:password@host1:33").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.isUseSsl()).isTrue(); + }); } @Test void withoutUsernameWithPasswordContainingColon() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis://:pass:word@host1:33").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isNullOrEmpty(); - assertThat(cf.getPassword()).isEqualTo("pass:word"); - }); + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis://:pass:word@host1:33").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isEqualTo("pass:word"); + }); } @Test void withUsernameWithPasswordStartsWithColonAndContainsColon() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis://user::pass:word@host1:33").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo(":pass:word"); - }); + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis://user::pass:word@host1:33").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo(":pass:word"); + }); } } @@ -112,29 +116,31 @@ class RedisStandaloneUrlWithNonStandardScheme { @Test void withAltSsl() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis+ssl://user:password@host1:33/7").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.isUseSsl()).isTrue(); - }); + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis+ssl://user:password@host1:33/7").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isTrue(); + }); } @Test void withAltTls() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis+tls://user:password@host1:33/7").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("host1"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(getUserName(cf)).isEqualTo("user"); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.getDatabase()).isEqualTo(7); - assertThat(cf.isUseSsl()).isTrue(); - assertThat(cf.isStartTls()).isTrue(); - }); + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis+tls://user:password@host1:33/7").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("host1"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(getUserName(cf)).isEqualTo("user"); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.getDatabase()).isEqualTo(7); + assertThat(cf.isUseSsl()).isTrue(); + assertThat(cf.isStartTls()).isTrue(); + }); } @Test @@ -156,9 +162,11 @@ void withPropsSetOnUrlIgnoresPropsSetInConfig() { @Test void withClientOptionPropsSetOnUrlOverridesAndRespectsPropsSetInConfig() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis+ssl://host1?timeout=47s&clientName=zuser", - "spring.redis.timeout=1200", "spring.redis.connect-timeout=2400", - "spring.redis.lettuce.shutdown-timeout=3600").run((context) -> { + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis+ssl://host1?timeout=47s&clientName=zuser", + "spring.redis.timeout=1200", "spring.redis.connect-timeout=2400", + "spring.redis.lettuce.shutdown-timeout=3600") + .run((context) -> { LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); assertThat(cf.getClientName()).isEqualTo("zuser"); assertThat(cf.getTimeout()).isEqualTo(47000); @@ -170,14 +178,15 @@ void withClientOptionPropsSetOnUrlOverridesAndRespectsPropsSetInConfig() { @Test void withoutClientOptionPropsSetOnUrlUsesDefaultCientOptions() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis+ssl://host1").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(cf.getClientName()).isNullOrEmpty(); - assertThat(cf.getTimeout()).isEqualTo(60000); - assertThat(cf.getShutdownTimeout()).isEqualTo(100); - assertThat(cf.getClientConfiguration().getClientOptions().get().getSocketOptions().getConnectTimeout() - .toMillis()).isEqualTo(10000); - }); + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis+ssl://host1").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(cf.getClientName()).isNullOrEmpty(); + assertThat(cf.getTimeout()).isEqualTo(60000); + assertThat(cf.getShutdownTimeout()).isEqualTo(100); + assertThat(cf.getClientConfiguration().getClientOptions().get().getSocketOptions() + .getConnectTimeout().toMillis()).isEqualTo(10000); + }); } } @@ -187,7 +196,8 @@ class RedisSentinelUrl { @Test void withMinimalFields() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis-sentinel://127.0.0.1?sentinelMasterId=5150") + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis-sentinel://127.0.0.1?sentinelMasterId=5150") .run((context) -> { LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); assertThat(getUserName(cf)).isNullOrEmpty(); @@ -225,7 +235,8 @@ void withAllFields() { @Test void withSsl() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=rediss-sentinel://127.0.0.1?sentinelMasterId=5150") + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=rediss-sentinel://127.0.0.1?sentinelMasterId=5150") .run((context) -> { LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class); assertThat(connectionFactory.isRedisSentinelAware()).isTrue(); @@ -259,19 +270,21 @@ class RedisSocketUrl { @Test void withMinimalFields() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis-socket:///mysocket").run((context) -> { - LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); - assertThat(getUserName(cf)).isNullOrEmpty(); - assertThat(cf.getPassword()).isNullOrEmpty(); - assertThat(cf.getDatabase()).isEqualTo(0); - assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) - .isEqualTo("/mysocket"); - }); + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis-socket:///mysocket").run((context) -> { + LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); + assertThat(getUserName(cf)).isNullOrEmpty(); + assertThat(cf.getPassword()).isNullOrEmpty(); + assertThat(cf.getDatabase()).isEqualTo(0); + assertThat(cf.getSocketConfiguration()).extracting(RedisSocketConfiguration::getSocket) + .isEqualTo("/mysocket"); + }); } @Test void withAllFields() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis-socket://user:password@/mysocket?database=7") + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis-socket://user:password@/mysocket?database=7") .run((context) -> { LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); assertThat(getUserName(cf)).isEqualTo("user"); @@ -284,7 +297,8 @@ void withAllFields() { @Test void withAltScheme() { - RedisAutoConfigurationLettuceUrlTests.this.contextRunner.withPropertyValues("spring.redis.url=redis+socket://user:password@/mysocket?database=7") + RedisAutoConfigurationLettuceUrlTests.this.contextRunner + .withPropertyValues("spring.redis.url=redis+socket://user:password@/mysocket?database=7") .run((context) -> { LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class); assertThat(getUserName(cf)).isEqualTo("user"); diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc index b56f42c5db12..faf7c1f5a4fa 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc @@ -49,7 +49,7 @@ additional settings as shown in the following example: spring: data: redis: - url: "redis://user:secret@redis.example.com:6399 + url: "redis://user:secret@redis.example.com:6379" ---- Alternatively, you can specify connection details using discrete properties. For example, you might @@ -61,7 +61,7 @@ declare the following settings in your `application.yml`: data: redis: host: "redis.example.com" - port: 6399 + port: 6379 username: "user" password: "secret" ---- From 9b6fd8d8a8cdcea97ababf537856e860da9ef3dd Mon Sep 17 00:00:00 2001 From: bono007 Date: Tue, 19 Oct 2021 10:01:38 -0500 Subject: [PATCH 7/7] Wrong path to spring.redis.url in nosql.adoc --- .../src/docs/asciidoc/data/nosql.adoc | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc index faf7c1f5a4fa..7135c7103109 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/data/nosql.adoc @@ -41,15 +41,14 @@ The following listing shows an example of such a bean: include::{docs-java}/data/nosql/redis/connecting/MyBean.java[] ---- -You can set the configprop:spring.data.redis.url[] property to change the URL and configure +You can set the configprop:spring.redis.url[] property to change the URL and configure additional settings as shown in the following example: [source,yaml,indent=0,subs="verbatim",configprops,configblocks] ---- - spring: - data: - redis: - url: "redis://user:secret@redis.example.com:6379" + spring: + redis: + url: "redis://user:secret@redis.example.com:6379" ---- Alternatively, you can specify connection details using discrete properties. For example, you might @@ -58,12 +57,11 @@ declare the following settings in your `application.yml`: [source,yaml,indent=0,subs="verbatim",configprops,configblocks] ---- spring: - data: - redis: - host: "redis.example.com" - port: 6379 - username: "user" - password: "secret" + redis: + host: "redis.example.com" + port: 6379 + username: "user" + password: "secret" ---- When using both the URL and the discrete properties, any values that are set in the URL will take precedence over the values set in the properties.