Skip to content

Commit 4a2abdc

Browse files
mp911dechristophstrobl
authored andcommitted
Provide TLS support for Sentinel clients in Jedis driver.
Closes #1560 Original Pull Request: #2021
1 parent c547ba2 commit 4a2abdc

File tree

2 files changed

+84
-67
lines changed

2 files changed

+84
-67
lines changed

Diff for: src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java

+28-22
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,7 @@
1515
*/
1616
package org.springframework.data.redis.connection.jedis;
1717

18-
import redis.clients.jedis.BinaryJedis;
19-
import redis.clients.jedis.BinaryJedisPubSub;
20-
import redis.clients.jedis.Client;
21-
import redis.clients.jedis.Connection;
22-
import redis.clients.jedis.Jedis;
23-
import redis.clients.jedis.MultiKeyPipelineBase;
24-
import redis.clients.jedis.Pipeline;
25-
import redis.clients.jedis.Response;
26-
import redis.clients.jedis.Transaction;
18+
import redis.clients.jedis.*;
2719
import redis.clients.jedis.exceptions.JedisDataException;
2820
import redis.clients.jedis.util.Pool;
2921

@@ -49,7 +41,6 @@
4941
import org.springframework.lang.Nullable;
5042
import org.springframework.util.Assert;
5143
import org.springframework.util.CollectionUtils;
52-
import org.springframework.util.StringUtils;
5344

5445
/**
5546
* {@code RedisConnection} implementation on top of <a href="https://github.com/xetorthio/jedis">Jedis</a> library.
@@ -81,6 +72,8 @@ public class JedisConnection extends AbstractRedisConnection {
8172

8273
private final @Nullable Pool<Jedis> pool;
8374
private final String clientName;
75+
private final JedisClientConfig nodeConfig;
76+
private final JedisClientConfig sentinelConfig;
8477

8578
private List<JedisResult> pipelinedResults = new ArrayList<>();
8679
private Queue<FutureResult<Response<?>>> txResults = new LinkedList<>();
@@ -120,18 +113,38 @@ public JedisConnection(Jedis jedis, Pool<Jedis> pool, int dbIndex) {
120113
* @param clientName the client name, can be {@literal null}.
121114
* @since 1.8
122115
*/
123-
protected JedisConnection(Jedis jedis, @Nullable Pool<Jedis> pool, int dbIndex, String clientName) {
116+
protected JedisConnection(Jedis jedis, @Nullable Pool<Jedis> pool, int dbIndex, @Nullable String clientName) {
117+
this(jedis, pool, createConfig(dbIndex, clientName), createConfig(dbIndex, clientName));
118+
}
119+
120+
private static DefaultJedisClientConfig createConfig(int dbIndex, @Nullable String clientName) {
121+
return DefaultJedisClientConfig.builder().databse(dbIndex).clientName(clientName).build();
122+
}
123+
124+
/**
125+
* Constructs a new <code>JedisConnection</code> instance backed by a jedis pool.
126+
*
127+
* @param jedis
128+
* @param pool can be null, if no pool is used
129+
* @param nodeConfig node configuration
130+
* @param sentinelConfig sentinel configuration
131+
* @since 2.5
132+
*/
133+
protected JedisConnection(Jedis jedis, @Nullable Pool<Jedis> pool, JedisClientConfig nodeConfig,
134+
JedisClientConfig sentinelConfig) {
124135

125136
this.jedis = jedis;
126137
this.pool = pool;
127-
this.clientName = clientName;
138+
this.clientName = nodeConfig.getClientName();
139+
this.nodeConfig = nodeConfig;
140+
this.sentinelConfig = sentinelConfig;
128141

129142
// select the db
130143
// if this fail, do manual clean-up before propagating the exception
131144
// as we're inside the constructor
132-
if (dbIndex != jedis.getDB()) {
145+
if (nodeConfig.getDatabase() != jedis.getDB()) {
133146
try {
134-
select(dbIndex);
147+
select(nodeConfig.getDatabase());
135148
} catch (DataAccessException ex) {
136149
close();
137150
throw ex;
@@ -776,14 +789,7 @@ protected JedisSentinelConnection getSentinelConnection(RedisNode sentinel) {
776789
}
777790

778791
protected Jedis getJedis(RedisNode node) {
779-
780-
Jedis jedis = new Jedis(node.getHost(), node.getPort());
781-
782-
if (StringUtils.hasText(clientName)) {
783-
jedis.clientSetname(clientName);
784-
}
785-
786-
return jedis;
792+
return new Jedis(new HostAndPort(node.getHost(), node.getPort()), this.sentinelConfig);
787793
}
788794

789795
@Nullable

Diff for: src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java

+56-45
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
*/
1616
package org.springframework.data.redis.connection.jedis;
1717

18-
import redis.clients.jedis.Client;
18+
import redis.clients.jedis.DefaultJedisClientConfig;
1919
import redis.clients.jedis.HostAndPort;
2020
import redis.clients.jedis.Jedis;
21+
import redis.clients.jedis.JedisClientConfig;
2122
import redis.clients.jedis.JedisCluster;
2223
import redis.clients.jedis.JedisPool;
2324
import redis.clients.jedis.JedisPoolConfig;
@@ -41,6 +42,7 @@
4142
import org.apache.commons.logging.Log;
4243
import org.apache.commons.logging.LogFactory;
4344
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
45+
4446
import org.springframework.beans.factory.DisposableBean;
4547
import org.springframework.beans.factory.InitializingBean;
4648
import org.springframework.dao.DataAccessException;
@@ -58,6 +60,7 @@
5860
import org.springframework.util.Assert;
5961
import org.springframework.util.ClassUtils;
6062
import org.springframework.util.CollectionUtils;
63+
import org.springframework.util.ObjectUtils;
6164
import org.springframework.util.StringUtils;
6265

6366
/**
@@ -88,6 +91,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
8891

8992
private final JedisClientConfiguration clientConfiguration;
9093
private @Nullable JedisShardInfo shardInfo;
94+
private JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().build();
9195
private boolean providedShardInfo = false;
9296
private @Nullable Pool<Jedis> pool;
9397
private boolean convertPipelineAndTxResults = true;
@@ -290,17 +294,7 @@ private Jedis createJedis() {
290294
return new Jedis(getShardInfo());
291295
}
292296

293-
Jedis jedis = new Jedis(getHostName(), getPort(), getConnectTimeout(), getReadTimeout(), isUseSsl(),
294-
clientConfiguration.getSslSocketFactory().orElse(null), //
295-
clientConfiguration.getSslParameters().orElse(null), //
296-
clientConfiguration.getHostnameVerifier().orElse(null));
297-
298-
Client client = jedis.getClient();
299-
300-
getRedisPassword().map(String::new).ifPresent(client::setPassword);
301-
client.setDb(getDatabase());
302-
303-
return jedis;
297+
return new Jedis(new HostAndPort(getHostName(), getPort()), this.clientConfig);
304298
}
305299

306300
/**
@@ -320,6 +314,8 @@ protected JedisConnection postProcessConnection(JedisConnection connection) {
320314
*/
321315
public void afterPropertiesSet() {
322316

317+
clientConfig = createClientConfig(getRedisUsername(), getRedisPassword());
318+
323319
if (shardInfo == null && clientConfiguration instanceof MutableJedisClientConfiguration) {
324320

325321
providedShardInfo = false;
@@ -357,6 +353,33 @@ public void afterPropertiesSet() {
357353
}
358354
}
359355

356+
private JedisClientConfig createClientConfig(@Nullable String username, RedisPassword password) {
357+
358+
DefaultJedisClientConfig.Builder builder = DefaultJedisClientConfig.builder();
359+
360+
clientConfiguration.getClientName().ifPresent(builder::clientName);
361+
builder.connectionTimeoutMillis(getConnectTimeout());
362+
builder.socketTimeoutMillis(getReadTimeout());
363+
364+
builder.databse(getDatabase());
365+
366+
if (!ObjectUtils.isEmpty(username)) {
367+
builder.user(username);
368+
}
369+
password.toOptional().map(String::new).ifPresent(builder::password);
370+
371+
if (isUseSsl()) {
372+
373+
builder.ssl(true);
374+
375+
clientConfiguration.getSslSocketFactory().ifPresent(builder::sslSocketFactory);
376+
clientConfiguration.getHostnameVerifier().ifPresent(builder::hostnameVerifier);
377+
clientConfiguration.getSslParameters().ifPresent(builder::sslParameters);
378+
}
379+
380+
return builder.build();
381+
}
382+
360383
private Pool<Jedis> createPool() {
361384

362385
if (isRedisSentinelAware()) {
@@ -374,13 +397,12 @@ private Pool<Jedis> createPool() {
374397
*/
375398
protected Pool<Jedis> createRedisSentinelPool(RedisSentinelConfiguration config) {
376399

377-
GenericObjectPoolConfig<?> poolConfig = getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig();
400+
GenericObjectPoolConfig<Jedis> poolConfig = getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig();
378401
String sentinelUser = null;
379-
String sentinelPassword = config.getSentinelPassword().toOptional().map(String::new).orElse(null);
380402

403+
JedisClientConfig sentinelConfig = createClientConfig(sentinelUser, config.getSentinelPassword());
381404
return new JedisSentinelPool(config.getMaster().getName(), convertToJedisSentinelSet(config.getSentinels()),
382-
poolConfig, getConnectTimeout(), getReadTimeout(), getUsername(), getPassword(), getDatabase(), getClientName(),
383-
getConnectTimeout(), getReadTimeout(), sentinelUser, sentinelPassword, getClientName());
405+
poolConfig, this.clientConfig, sentinelConfig);
384406
}
385407

386408
/**
@@ -390,12 +412,7 @@ poolConfig, getConnectTimeout(), getReadTimeout(), getUsername(), getPassword(),
390412
* @since 1.4
391413
*/
392414
protected Pool<Jedis> createRedisPool() {
393-
394-
return new JedisPool(getPoolConfig(), getHostName(), getPort(), getConnectTimeout(), getReadTimeout(),
395-
getUsername(), getPassword(), getDatabase(), getClientName(), isUseSsl(),
396-
clientConfiguration.getSslSocketFactory().orElse(null), //
397-
clientConfiguration.getSslParameters().orElse(null), //
398-
clientConfiguration.getHostnameVerifier().orElse(null));
415+
return new JedisPool(getPoolConfig(), new HostAndPort(getHostName(), getPort()), this.clientConfig);
399416
}
400417

401418
private JedisCluster createCluster() {
@@ -423,7 +440,8 @@ protected ClusterTopologyProvider createTopologyProvider(JedisCluster cluster) {
423440
* @return the actual {@link JedisCluster}.
424441
* @since 1.7
425442
*/
426-
protected JedisCluster createCluster(RedisClusterConfiguration clusterConfig, GenericObjectPoolConfig<?> poolConfig) {
443+
protected JedisCluster createCluster(RedisClusterConfiguration clusterConfig,
444+
GenericObjectPoolConfig<Jedis> poolConfig) {
427445

428446
Assert.notNull(clusterConfig, "Cluster configuration must not be null!");
429447

@@ -434,10 +452,7 @@ protected JedisCluster createCluster(RedisClusterConfiguration clusterConfig, Ge
434452

435453
int redirects = clusterConfig.getMaxRedirects() != null ? clusterConfig.getMaxRedirects() : 5;
436454

437-
return new JedisCluster(hostAndPort, getConnectTimeout(), getReadTimeout(), redirects, getUsername(), getPassword(),
438-
getClientName(), poolConfig, isUseSsl(), clientConfiguration.getSslSocketFactory().orElse(null),
439-
clientConfiguration.getSslParameters().orElse(null), clientConfiguration.getHostnameVerifier().orElse(null),
440-
null);
455+
return new JedisCluster(hostAndPort, this.clientConfig, redirects, poolConfig);
441456
}
442457

443458
/*
@@ -553,16 +568,6 @@ public void setUseSsl(boolean useSsl) {
553568
getMutableConfiguration().setUseSsl(useSsl);
554569
}
555570

556-
/**
557-
* Returns the username used for authenticating with the Redis server.
558-
*
559-
* @return username for authentication.
560-
*/
561-
@Nullable
562-
private String getUsername() {
563-
return getRedisUsername();
564-
}
565-
566571
/**
567572
* Returns the password used for authenticating with the Redis server.
568573
*
@@ -715,7 +720,7 @@ public void setUsePool(boolean usePool) {
715720
* @return the poolConfig
716721
*/
717722
@Nullable
718-
public GenericObjectPoolConfig getPoolConfig() {
723+
public GenericObjectPoolConfig<Jedis> getPoolConfig() {
719724
return clientConfiguration.getPoolConfig().orElse(null);
720725
}
721726

@@ -877,35 +882,41 @@ private Jedis getActiveSentinel() {
877882
Assert.isTrue(RedisConfiguration.isSentinelConfiguration(configuration), "SentinelConfig must not be null!");
878883
SentinelConfiguration sentinelConfiguration = (SentinelConfiguration) configuration;
879884

885+
JedisClientConfig clientConfig = createClientConfig(null, sentinelConfiguration.getSentinelPassword());
880886
for (RedisNode node : sentinelConfiguration.getSentinels()) {
881887

882-
Jedis jedis = new Jedis(node.getHost(), node.getPort(), getConnectTimeout(), getReadTimeout());
883-
sentinelConfiguration.getSentinelPassword().toOptional().map(String::new).ifPresent(jedis::auth);
888+
Jedis jedis = null;
889+
boolean success = false;
884890

885891
try {
886-
if (jedis.ping().equalsIgnoreCase("pong")) {
887892

888-
potentiallySetClientName(jedis);
893+
jedis = new Jedis(new HostAndPort(node.getHost(), node.getPort()), clientConfig);
894+
if (jedis.ping().equalsIgnoreCase("pong")) {
895+
success = true;
889896
return jedis;
890897
}
891898
} catch (Exception ex) {
892899
log.warn(String.format("Ping failed for sentinel host:%s", node.getHost()), ex);
900+
} finally {
901+
if (!success && jedis != null) {
902+
jedis.close();
903+
}
893904
}
894905
}
895906

896907
throw new InvalidDataAccessResourceUsageException("No Sentinel found");
897908
}
898909

899-
private Set<String> convertToJedisSentinelSet(Collection<RedisNode> nodes) {
910+
private static Set<HostAndPort> convertToJedisSentinelSet(Collection<RedisNode> nodes) {
900911

901912
if (CollectionUtils.isEmpty(nodes)) {
902913
return Collections.emptySet();
903914
}
904915

905-
Set<String> convertedNodes = new LinkedHashSet<>(nodes.size());
916+
Set<HostAndPort> convertedNodes = new LinkedHashSet<>(nodes.size());
906917
for (RedisNode node : nodes) {
907918
if (node != null) {
908-
convertedNodes.add(node.asString());
919+
convertedNodes.add(new HostAndPort(node.getHost(), node.getPort()));
909920
}
910921
}
911922
return convertedNodes;

0 commit comments

Comments
 (0)