diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java
index e04f45bac7..9eca99886f 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java
@@ -61,6 +61,7 @@
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -97,6 +98,7 @@
* @author Ruben Cervilla
* @author Luis De Bello
* @author Andrea Como
+ * @author Chris Bono
*/
public class LettuceConnectionFactory
implements InitializingBean, DisposableBean, RedisConnectionFactory, ReactiveRedisConnectionFactory {
@@ -279,6 +281,32 @@ public LettuceConnectionFactory(RedisClusterConfiguration clusterConfiguration,
this.configuration = clusterConfiguration;
}
+ /**
+ * Creates a {@link RedisConfiguration} based on a {@link RedisURI} according to the following:
+ *
+ * - If {@code redisURI} has sentinel info a {@link RedisSentinelConfiguration} is returned
+ * - If {@code redisURI} has socket info a {@link RedisSocketConfiguration} is returned
+ * - Otherwise a {@link RedisStandaloneConfiguration} is returned
+ *
+ *
+ * @param redisURI the connection info in the format of a RedisURI
+ * @return an appropriate {@link RedisConfiguration} instance representing the Redis URI.
+ */
+ public static RedisConfiguration createRedisConfiguration(RedisURI redisURI) {
+
+ Assert.notNull(redisURI, "RedisURI must not be null");
+
+ if (!ObjectUtils.isEmpty(redisURI.getSentinels())) {
+ return LettuceConverters.redisUriToSentinelConfiguration(redisURI);
+ }
+
+ if (!ObjectUtils.isEmpty(redisURI.getSocket())) {
+ return LettuceConverters.redisUriToSocketConfiguration(redisURI);
+ }
+
+ return LettuceConverters.redisUriToStandaloneConfiguration(redisURI);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java
index 811893d7c6..e1740e68ab 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java
@@ -33,14 +33,12 @@
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
-import org.springframework.data.redis.connection.BitFieldSubCommands;
+import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldGet;
import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy;
import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow;
import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldSet;
import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldSubCommand;
-import org.springframework.data.redis.connection.DefaultTuple;
-import org.springframework.data.redis.connection.RedisClusterNode;
import org.springframework.data.redis.connection.RedisClusterNode.Flag;
import org.springframework.data.redis.connection.RedisClusterNode.LinkState;
import org.springframework.data.redis.connection.RedisClusterNode.SlotRange;
@@ -49,17 +47,10 @@
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.connection.RedisListCommands.Direction;
import org.springframework.data.redis.connection.RedisListCommands.Position;
-import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisNode.NodeType;
-import org.springframework.data.redis.connection.RedisPassword;
-import org.springframework.data.redis.connection.RedisSentinelConfiguration;
-import org.springframework.data.redis.connection.RedisServer;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
-import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.connection.RedisZSetCommands.Range.Boundary;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
-import org.springframework.data.redis.connection.ReturnType;
-import org.springframework.data.redis.connection.SortParameters;
import org.springframework.data.redis.connection.SortParameters.Order;
import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.data.redis.connection.convert.ListConverter;
@@ -85,6 +76,7 @@
* @author Mark Paluch
* @author Ninad Divadkar
* @author dengliming
+ * @author Chris Bono
*/
public abstract class LettuceConverters extends Converters {
@@ -535,6 +527,86 @@ public static RedisURI sentinelConfigurationToRedisURI(RedisSentinelConfiguratio
return builder.build();
}
+ /**
+ * Converts a {@link RedisURI} to its corresponding {@link RedisSentinelConfiguration}.
+ *
+ * @param redisURI the uri containing the Redis Sentinel connection info
+ * @return a {@link RedisSentinelConfiguration} representing the Redis Sentinel information in the Redis URI.
+ * @since 2.6
+ */
+ static RedisSentinelConfiguration redisUriToSentinelConfiguration(RedisURI redisURI) {
+
+ Assert.notNull(redisURI, "RedisURI is required");
+
+ RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration();
+ if (!ObjectUtils.isEmpty(redisURI.getSentinelMasterId())) {
+ sentinelConfiguration.setMaster(redisURI.getSentinelMasterId());
+ }
+ sentinelConfiguration.setDatabase(redisURI.getDatabase());
+
+ for (RedisURI sentinelNodeRedisUri : redisURI.getSentinels()) {
+ RedisNode sentinelNode = new RedisNode(sentinelNodeRedisUri.getHost(), sentinelNodeRedisUri.getPort());
+ if (sentinelNodeRedisUri.getPassword() != null) {
+ sentinelConfiguration.setSentinelPassword(sentinelNodeRedisUri.getPassword());
+ }
+ sentinelConfiguration.addSentinel(sentinelNode);
+ }
+
+ applyAuthentication(redisURI, sentinelConfiguration);
+
+ return sentinelConfiguration;
+ }
+
+ /**
+ * Converts a {@link RedisURI} to its corresponding {@link RedisSocketConfiguration}.
+ *
+ * @param redisURI the uri containing the Redis connection info using a local unix domain socket
+ * @return a {@link RedisSocketConfiguration} representing the connection information in the Redis URI.
+ * @since 2.6
+ */
+ static RedisSocketConfiguration redisUriToSocketConfiguration(RedisURI redisURI) {
+
+ Assert.notNull(redisURI, "RedisURI is required");
+
+ RedisSocketConfiguration socketConfiguration = new RedisSocketConfiguration();
+ socketConfiguration.setSocket(redisURI.getSocket());
+ socketConfiguration.setDatabase(redisURI.getDatabase());
+
+ applyAuthentication(redisURI, socketConfiguration);
+
+ return socketConfiguration;
+ }
+
+ /**
+ * Converts a {@link RedisURI} to its corresponding {@link RedisStandaloneConfiguration}.
+ *
+ * @param redisURI the uri containing the Redis connection info
+ * @return a {@link RedisStandaloneConfiguration} representing the connection information in the Redis URI.
+ * @since 2.6
+ */
+ static RedisStandaloneConfiguration redisUriToStandaloneConfiguration(RedisURI redisURI) {
+
+ Assert.notNull(redisURI, "RedisURI is required");
+
+ RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration();
+ standaloneConfiguration.setHostName(redisURI.getHost());
+ standaloneConfiguration.setPort(redisURI.getPort());
+ standaloneConfiguration.setDatabase(redisURI.getDatabase());
+
+ applyAuthentication(redisURI, standaloneConfiguration);
+
+ return standaloneConfiguration;
+ }
+
+ private static void applyAuthentication(RedisURI redisURI, RedisConfiguration.WithAuthentication redisConfiguration) {
+ if (StringUtils.hasText(redisURI.getUsername())) {
+ redisConfiguration.setUsername(redisURI.getUsername());
+ }
+ if (redisURI.getPassword() != null) {
+ redisConfiguration.setPassword(redisURI.getPassword());
+ }
+ }
+
public static byte[] toBytes(@Nullable String source) {
if (source == null) {
return null;
diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryCreateRedisConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryCreateRedisConfigurationUnitTests.java
new file mode 100644
index 0000000000..e14f54df03
--- /dev/null
+++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryCreateRedisConfigurationUnitTests.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2015-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.data.redis.connection.lettuce;
+
+import static org.assertj.core.api.Assertions.*;
+
+import io.lettuce.core.RedisURI;
+
+import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.data.redis.connection.RedisConfiguration;
+import org.springframework.data.redis.connection.RedisNode;
+import org.springframework.data.redis.connection.RedisSentinelConfiguration;
+import org.springframework.data.redis.connection.RedisSocketConfiguration;
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
+
+/**
+ * Unit tests for the {@link LettuceConnectionFactory#createRedisConfiguration(RedisURI)} factory method.
+ *
+ * @author Chris Bono
+ */
+class LettuceConnectionFactoryCreateRedisConfigurationUnitTests {
+
+ @Test
+ void requiresRedisURI() {
+ assertThatThrownBy(() -> LettuceConnectionFactory.createRedisConfiguration(null))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("RedisURI must not be null");
+ }
+
+ @Nested // GH-2117
+ class CreateRedisSentinelConfiguration {
+
+ @Test
+ void minimalFieldsSetOnRedisURI() {
+ RedisURI redisURI = RedisURI.create("redis-sentinel://myserver?sentinelMasterId=5150");
+ RedisSentinelConfiguration expectedSentinelConfiguration = new RedisSentinelConfiguration();
+ expectedSentinelConfiguration.setMaster("5150");
+ expectedSentinelConfiguration.addSentinel(new RedisNode("myserver", 26379));
+
+ RedisConfiguration sentinelConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
+
+ assertThat(sentinelConfiguration).usingRecursiveComparison(sentinelCompareConfig())
+ .isInstanceOf(RedisSentinelConfiguration.class)
+ .isEqualTo(expectedSentinelConfiguration);
+ }
+
+ @Test
+ void allFieldsSetOnRedisURI() {
+ RedisURI redisURI = RedisURI.create("redis-sentinel://fooUser:fooPass@myserver1:111,myserver2:222/7?sentinelMasterId=5150");
+ // Set the passwords directly on the sentinels so that it gets picked up by converter
+ char[] sentinelPass = "changeme".toCharArray();
+ redisURI.getSentinels().forEach(sentinelRedisUri -> sentinelRedisUri.setPassword(sentinelPass));
+ RedisSentinelConfiguration expectedSentinelConfiguration = new RedisSentinelConfiguration();
+ expectedSentinelConfiguration.setMaster("5150");
+ expectedSentinelConfiguration.setDatabase(7);
+ expectedSentinelConfiguration.setUsername("fooUser");
+ expectedSentinelConfiguration.setPassword("fooPass");
+ expectedSentinelConfiguration.setSentinelPassword(sentinelPass);
+ expectedSentinelConfiguration.addSentinel(new RedisNode("myserver1", 111));
+ expectedSentinelConfiguration.addSentinel(new RedisNode("myserver2", 222));
+
+ RedisConfiguration sentinelConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
+
+ assertThat(sentinelConfiguration).usingRecursiveComparison(sentinelCompareConfig())
+ .isInstanceOf(RedisSentinelConfiguration.class)
+ .isEqualTo(expectedSentinelConfiguration);
+ }
+
+ // RedisSentinelConfiguration does not provide equals impl
+ private RecursiveComparisonConfiguration sentinelCompareConfig() {
+ return RecursiveComparisonConfiguration.builder().withComparedFields(
+ "master",
+ "username",
+ "password",
+ "sentinelPassword",
+ "database",
+ "sentinels")
+ .build();
+ }
+ }
+
+ @Nested // GH-2117
+ class CreateRedisSocketConfiguration {
+
+ @Test
+ void minimalFieldsSetOnRedisURI() {
+ RedisURI redisURI = RedisURI.builder()
+ .socket("mysocket")
+ .build();
+ RedisSocketConfiguration expectedSocketConfiguration = new RedisSocketConfiguration();
+ expectedSocketConfiguration.setSocket("mysocket");
+
+ RedisConfiguration socketConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
+
+ assertThat(socketConfiguration).usingRecursiveComparison(socketCompareConfig())
+ .isInstanceOf(RedisSocketConfiguration.class)
+ .isEqualTo(expectedSocketConfiguration);
+ }
+
+ @Test
+ void allFieldsSetOnRedisURI() {
+ RedisURI redisURI = RedisURI.builder()
+ .socket("mysocket")
+ .withAuthentication("fooUser", "fooPass".toCharArray())
+ .withDatabase(7)
+ .build();
+ RedisSocketConfiguration expectedSocketConfiguration = new RedisSocketConfiguration();
+ expectedSocketConfiguration.setSocket("mysocket");
+ expectedSocketConfiguration.setUsername("fooUser");
+ expectedSocketConfiguration.setPassword("fooPass");
+ expectedSocketConfiguration.setDatabase(7);
+
+ RedisConfiguration socketConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
+
+ assertThat(socketConfiguration).usingRecursiveComparison(socketCompareConfig())
+ .isInstanceOf(RedisSocketConfiguration.class)
+ .isEqualTo(expectedSocketConfiguration);
+ }
+
+ // RedisSocketConfiguration does not provide equals impl
+ private RecursiveComparisonConfiguration socketCompareConfig() {
+ return RecursiveComparisonConfiguration.builder().withComparedFields(
+ "socket",
+ "username",
+ "password",
+ "database")
+ .build();
+ }
+ }
+
+ @Nested // GH-2117
+ class CreateRedisStandaloneConfiguration {
+
+ @Test
+ void minimalFieldsSetOnRedisURI() {
+ RedisURI redisURI = RedisURI.create("redis://myserver");
+ RedisStandaloneConfiguration expectedStandaloneConfiguration = new RedisStandaloneConfiguration();
+ expectedStandaloneConfiguration.setHostName("myserver");
+
+ RedisConfiguration StandaloneConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
+
+ assertThat(StandaloneConfiguration).usingRecursiveComparison(standaloneCompareConfig())
+ .isInstanceOf(RedisStandaloneConfiguration.class)
+ .isEqualTo(expectedStandaloneConfiguration);
+ }
+
+ @Test
+ void allFieldsSetOnRedisURI() {
+ RedisURI redisURI = RedisURI.create("redis://fooUser:fooPass@myserver1:111/7");
+ RedisStandaloneConfiguration expectedStandaloneConfiguration = new RedisStandaloneConfiguration();
+ expectedStandaloneConfiguration.setHostName("myserver1");
+ expectedStandaloneConfiguration.setPort(111);
+ expectedStandaloneConfiguration.setDatabase(7);
+ expectedStandaloneConfiguration.setUsername("fooUser");
+ expectedStandaloneConfiguration.setPassword("fooPass");
+
+ RedisConfiguration StandaloneConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
+
+ assertThat(StandaloneConfiguration).usingRecursiveComparison(standaloneCompareConfig())
+ .isInstanceOf(RedisStandaloneConfiguration.class)
+ .isEqualTo(expectedStandaloneConfiguration);
+
+ }
+
+ // RedisStandaloneConfiguration does not provide equals impl
+ private RecursiveComparisonConfiguration standaloneCompareConfig() {
+ return RecursiveComparisonConfiguration.builder().withComparedFields(
+ "host",
+ "port",
+ "username",
+ "password",
+ "database")
+ .build();
+ }
+ }
+
+}