Skip to content

Commit 19e9e74

Browse files
onobcmp911de
authored andcommitted
Add factory method to LettuceConnectionFactory to create RedisConfiguration from RedisURI.
Closes #2116. Original pull request: #2117.
1 parent edde436 commit 19e9e74

File tree

3 files changed

+303
-10
lines changed

3 files changed

+303
-10
lines changed

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java

+28
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import org.springframework.lang.Nullable;
6262
import org.springframework.util.Assert;
6363
import org.springframework.util.ClassUtils;
64+
import org.springframework.util.ObjectUtils;
6465
import org.springframework.util.StringUtils;
6566

6667
/**
@@ -97,6 +98,7 @@
9798
* @author Ruben Cervilla
9899
* @author Luis De Bello
99100
* @author Andrea Como
101+
* @author Chris Bono
100102
*/
101103
public class LettuceConnectionFactory
102104
implements InitializingBean, DisposableBean, RedisConnectionFactory, ReactiveRedisConnectionFactory {
@@ -279,6 +281,32 @@ public LettuceConnectionFactory(RedisClusterConfiguration clusterConfiguration,
279281
this.configuration = clusterConfiguration;
280282
}
281283

284+
/**
285+
* Creates a {@link RedisConfiguration} based on a {@link RedisURI} according to the following:
286+
* <ul>
287+
* <li>If {@code redisURI} has sentinel info a {@link RedisSentinelConfiguration} is returned</li>
288+
* <li>If {@code redisURI} has socket info a {@link RedisSocketConfiguration} is returned</li>
289+
* <li>Otherwise a {@link RedisStandaloneConfiguration} is returned</li>
290+
* </ul>
291+
*
292+
* @param redisURI the connection info in the format of a RedisURI
293+
* @return an appropriate {@link RedisConfiguration} instance representing the Redis URI.
294+
*/
295+
public static RedisConfiguration createRedisConfiguration(RedisURI redisURI) {
296+
297+
Assert.notNull(redisURI, "RedisURI must not be null");
298+
299+
if (!ObjectUtils.isEmpty(redisURI.getSentinels())) {
300+
return LettuceConverters.redisUriToSentinelConfiguration(redisURI);
301+
}
302+
303+
if (!ObjectUtils.isEmpty(redisURI.getSocket())) {
304+
return LettuceConverters.redisUriToSocketConfiguration(redisURI);
305+
}
306+
307+
return LettuceConverters.redisUriToStandaloneConfiguration(redisURI);
308+
}
309+
282310
/*
283311
* (non-Javadoc)
284312
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java

+82-10
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,12 @@
3333
import org.springframework.data.geo.Metric;
3434
import org.springframework.data.geo.Metrics;
3535
import org.springframework.data.geo.Point;
36-
import org.springframework.data.redis.connection.BitFieldSubCommands;
36+
import org.springframework.data.redis.connection.*;
3737
import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldGet;
3838
import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy;
3939
import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow;
4040
import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldSet;
4141
import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldSubCommand;
42-
import org.springframework.data.redis.connection.DefaultTuple;
43-
import org.springframework.data.redis.connection.RedisClusterNode;
4442
import org.springframework.data.redis.connection.RedisClusterNode.Flag;
4543
import org.springframework.data.redis.connection.RedisClusterNode.LinkState;
4644
import org.springframework.data.redis.connection.RedisClusterNode.SlotRange;
@@ -49,17 +47,10 @@
4947
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
5048
import org.springframework.data.redis.connection.RedisListCommands.Direction;
5149
import org.springframework.data.redis.connection.RedisListCommands.Position;
52-
import org.springframework.data.redis.connection.RedisNode;
5350
import org.springframework.data.redis.connection.RedisNode.NodeType;
54-
import org.springframework.data.redis.connection.RedisPassword;
55-
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
56-
import org.springframework.data.redis.connection.RedisServer;
5751
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
58-
import org.springframework.data.redis.connection.RedisZSetCommands;
5952
import org.springframework.data.redis.connection.RedisZSetCommands.Range.Boundary;
6053
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
61-
import org.springframework.data.redis.connection.ReturnType;
62-
import org.springframework.data.redis.connection.SortParameters;
6354
import org.springframework.data.redis.connection.SortParameters.Order;
6455
import org.springframework.data.redis.connection.convert.Converters;
6556
import org.springframework.data.redis.connection.convert.ListConverter;
@@ -85,6 +76,7 @@
8576
* @author Mark Paluch
8677
* @author Ninad Divadkar
8778
* @author dengliming
79+
* @author Chris Bono
8880
*/
8981
public abstract class LettuceConverters extends Converters {
9082

@@ -535,6 +527,86 @@ public static RedisURI sentinelConfigurationToRedisURI(RedisSentinelConfiguratio
535527
return builder.build();
536528
}
537529

530+
/**
531+
* Converts a {@link RedisURI} to its corresponding {@link RedisSentinelConfiguration}.
532+
*
533+
* @param redisURI the uri containing the Redis Sentinel connection info
534+
* @return a {@link RedisSentinelConfiguration} representing the Redis Sentinel information in the Redis URI.
535+
* @since 2.6
536+
*/
537+
static RedisSentinelConfiguration redisUriToSentinelConfiguration(RedisURI redisURI) {
538+
539+
Assert.notNull(redisURI, "RedisURI is required");
540+
541+
RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration();
542+
if (!ObjectUtils.isEmpty(redisURI.getSentinelMasterId())) {
543+
sentinelConfiguration.setMaster(redisURI.getSentinelMasterId());
544+
}
545+
sentinelConfiguration.setDatabase(redisURI.getDatabase());
546+
547+
for (RedisURI sentinelNodeRedisUri : redisURI.getSentinels()) {
548+
RedisNode sentinelNode = new RedisNode(sentinelNodeRedisUri.getHost(), sentinelNodeRedisUri.getPort());
549+
if (sentinelNodeRedisUri.getPassword() != null) {
550+
sentinelConfiguration.setSentinelPassword(sentinelNodeRedisUri.getPassword());
551+
}
552+
sentinelConfiguration.addSentinel(sentinelNode);
553+
}
554+
555+
applyAuthentication(redisURI, sentinelConfiguration);
556+
557+
return sentinelConfiguration;
558+
}
559+
560+
/**
561+
* Converts a {@link RedisURI} to its corresponding {@link RedisSocketConfiguration}.
562+
*
563+
* @param redisURI the uri containing the Redis connection info using a local unix domain socket
564+
* @return a {@link RedisSocketConfiguration} representing the connection information in the Redis URI.
565+
* @since 2.6
566+
*/
567+
static RedisSocketConfiguration redisUriToSocketConfiguration(RedisURI redisURI) {
568+
569+
Assert.notNull(redisURI, "RedisURI is required");
570+
571+
RedisSocketConfiguration socketConfiguration = new RedisSocketConfiguration();
572+
socketConfiguration.setSocket(redisURI.getSocket());
573+
socketConfiguration.setDatabase(redisURI.getDatabase());
574+
575+
applyAuthentication(redisURI, socketConfiguration);
576+
577+
return socketConfiguration;
578+
}
579+
580+
/**
581+
* Converts a {@link RedisURI} to its corresponding {@link RedisStandaloneConfiguration}.
582+
*
583+
* @param redisURI the uri containing the Redis connection info
584+
* @return a {@link RedisStandaloneConfiguration} representing the connection information in the Redis URI.
585+
* @since 2.6
586+
*/
587+
static RedisStandaloneConfiguration redisUriToStandaloneConfiguration(RedisURI redisURI) {
588+
589+
Assert.notNull(redisURI, "RedisURI is required");
590+
591+
RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration();
592+
standaloneConfiguration.setHostName(redisURI.getHost());
593+
standaloneConfiguration.setPort(redisURI.getPort());
594+
standaloneConfiguration.setDatabase(redisURI.getDatabase());
595+
596+
applyAuthentication(redisURI, standaloneConfiguration);
597+
598+
return standaloneConfiguration;
599+
}
600+
601+
private static void applyAuthentication(RedisURI redisURI, RedisConfiguration.WithAuthentication redisConfiguration) {
602+
if (StringUtils.hasText(redisURI.getUsername())) {
603+
redisConfiguration.setUsername(redisURI.getUsername());
604+
}
605+
if (redisURI.getPassword() != null) {
606+
redisConfiguration.setPassword(redisURI.getPassword());
607+
}
608+
}
609+
538610
public static byte[] toBytes(@Nullable String source) {
539611
if (source == null) {
540612
return null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* Copyright 2015-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.connection.lettuce;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import io.lettuce.core.RedisURI;
21+
22+
import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration;
23+
import org.junit.jupiter.api.Nested;
24+
import org.junit.jupiter.api.Test;
25+
26+
import org.springframework.data.redis.connection.RedisConfiguration;
27+
import org.springframework.data.redis.connection.RedisNode;
28+
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
29+
import org.springframework.data.redis.connection.RedisSocketConfiguration;
30+
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
31+
32+
/**
33+
* Unit tests for the {@link LettuceConnectionFactory#createRedisConfiguration(RedisURI)} factory method.
34+
*
35+
* @author Chris Bono
36+
*/
37+
class LettuceConnectionFactoryCreateRedisConfigurationUnitTests {
38+
39+
@Test
40+
void requiresRedisURI() {
41+
assertThatThrownBy(() -> LettuceConnectionFactory.createRedisConfiguration(null))
42+
.isInstanceOf(IllegalArgumentException.class)
43+
.hasMessage("RedisURI must not be null");
44+
}
45+
46+
@Nested // GH-2117
47+
class CreateRedisSentinelConfiguration {
48+
49+
@Test
50+
void minimalFieldsSetOnRedisURI() {
51+
RedisURI redisURI = RedisURI.create("redis-sentinel://myserver?sentinelMasterId=5150");
52+
RedisSentinelConfiguration expectedSentinelConfiguration = new RedisSentinelConfiguration();
53+
expectedSentinelConfiguration.setMaster("5150");
54+
expectedSentinelConfiguration.addSentinel(new RedisNode("myserver", 26379));
55+
56+
RedisConfiguration sentinelConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
57+
58+
assertThat(sentinelConfiguration).usingRecursiveComparison(sentinelCompareConfig())
59+
.isInstanceOf(RedisSentinelConfiguration.class)
60+
.isEqualTo(expectedSentinelConfiguration);
61+
}
62+
63+
@Test
64+
void allFieldsSetOnRedisURI() {
65+
RedisURI redisURI = RedisURI.create("redis-sentinel://fooUser:fooPass@myserver1:111,myserver2:222/7?sentinelMasterId=5150");
66+
// Set the passwords directly on the sentinels so that it gets picked up by converter
67+
char[] sentinelPass = "changeme".toCharArray();
68+
redisURI.getSentinels().forEach(sentinelRedisUri -> sentinelRedisUri.setPassword(sentinelPass));
69+
RedisSentinelConfiguration expectedSentinelConfiguration = new RedisSentinelConfiguration();
70+
expectedSentinelConfiguration.setMaster("5150");
71+
expectedSentinelConfiguration.setDatabase(7);
72+
expectedSentinelConfiguration.setUsername("fooUser");
73+
expectedSentinelConfiguration.setPassword("fooPass");
74+
expectedSentinelConfiguration.setSentinelPassword(sentinelPass);
75+
expectedSentinelConfiguration.addSentinel(new RedisNode("myserver1", 111));
76+
expectedSentinelConfiguration.addSentinel(new RedisNode("myserver2", 222));
77+
78+
RedisConfiguration sentinelConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
79+
80+
assertThat(sentinelConfiguration).usingRecursiveComparison(sentinelCompareConfig())
81+
.isInstanceOf(RedisSentinelConfiguration.class)
82+
.isEqualTo(expectedSentinelConfiguration);
83+
}
84+
85+
// RedisSentinelConfiguration does not provide equals impl
86+
private RecursiveComparisonConfiguration sentinelCompareConfig() {
87+
return RecursiveComparisonConfiguration.builder().withComparedFields(
88+
"master",
89+
"username",
90+
"password",
91+
"sentinelPassword",
92+
"database",
93+
"sentinels")
94+
.build();
95+
}
96+
}
97+
98+
@Nested // GH-2117
99+
class CreateRedisSocketConfiguration {
100+
101+
@Test
102+
void minimalFieldsSetOnRedisURI() {
103+
RedisURI redisURI = RedisURI.builder()
104+
.socket("mysocket")
105+
.build();
106+
RedisSocketConfiguration expectedSocketConfiguration = new RedisSocketConfiguration();
107+
expectedSocketConfiguration.setSocket("mysocket");
108+
109+
RedisConfiguration socketConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
110+
111+
assertThat(socketConfiguration).usingRecursiveComparison(socketCompareConfig())
112+
.isInstanceOf(RedisSocketConfiguration.class)
113+
.isEqualTo(expectedSocketConfiguration);
114+
}
115+
116+
@Test
117+
void allFieldsSetOnRedisURI() {
118+
RedisURI redisURI = RedisURI.builder()
119+
.socket("mysocket")
120+
.withAuthentication("fooUser", "fooPass".toCharArray())
121+
.withDatabase(7)
122+
.build();
123+
RedisSocketConfiguration expectedSocketConfiguration = new RedisSocketConfiguration();
124+
expectedSocketConfiguration.setSocket("mysocket");
125+
expectedSocketConfiguration.setUsername("fooUser");
126+
expectedSocketConfiguration.setPassword("fooPass");
127+
expectedSocketConfiguration.setDatabase(7);
128+
129+
RedisConfiguration socketConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
130+
131+
assertThat(socketConfiguration).usingRecursiveComparison(socketCompareConfig())
132+
.isInstanceOf(RedisSocketConfiguration.class)
133+
.isEqualTo(expectedSocketConfiguration);
134+
}
135+
136+
// RedisSocketConfiguration does not provide equals impl
137+
private RecursiveComparisonConfiguration socketCompareConfig() {
138+
return RecursiveComparisonConfiguration.builder().withComparedFields(
139+
"socket",
140+
"username",
141+
"password",
142+
"database")
143+
.build();
144+
}
145+
}
146+
147+
@Nested // GH-2117
148+
class CreateRedisStandaloneConfiguration {
149+
150+
@Test
151+
void minimalFieldsSetOnRedisURI() {
152+
RedisURI redisURI = RedisURI.create("redis://myserver");
153+
RedisStandaloneConfiguration expectedStandaloneConfiguration = new RedisStandaloneConfiguration();
154+
expectedStandaloneConfiguration.setHostName("myserver");
155+
156+
RedisConfiguration StandaloneConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
157+
158+
assertThat(StandaloneConfiguration).usingRecursiveComparison(standaloneCompareConfig())
159+
.isInstanceOf(RedisStandaloneConfiguration.class)
160+
.isEqualTo(expectedStandaloneConfiguration);
161+
}
162+
163+
@Test
164+
void allFieldsSetOnRedisURI() {
165+
RedisURI redisURI = RedisURI.create("redis://fooUser:fooPass@myserver1:111/7");
166+
RedisStandaloneConfiguration expectedStandaloneConfiguration = new RedisStandaloneConfiguration();
167+
expectedStandaloneConfiguration.setHostName("myserver1");
168+
expectedStandaloneConfiguration.setPort(111);
169+
expectedStandaloneConfiguration.setDatabase(7);
170+
expectedStandaloneConfiguration.setUsername("fooUser");
171+
expectedStandaloneConfiguration.setPassword("fooPass");
172+
173+
RedisConfiguration StandaloneConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI);
174+
175+
assertThat(StandaloneConfiguration).usingRecursiveComparison(standaloneCompareConfig())
176+
.isInstanceOf(RedisStandaloneConfiguration.class)
177+
.isEqualTo(expectedStandaloneConfiguration);
178+
179+
}
180+
181+
// RedisStandaloneConfiguration does not provide equals impl
182+
private RecursiveComparisonConfiguration standaloneCompareConfig() {
183+
return RecursiveComparisonConfiguration.builder().withComparedFields(
184+
"host",
185+
"port",
186+
"username",
187+
"password",
188+
"database")
189+
.build();
190+
}
191+
}
192+
193+
}

0 commit comments

Comments
 (0)