DATAREDIS-574 - Introduce dedicated RedisClientConfiguration for Jedis & Lettuce.

We added Jedis-/LettuceClientConfiguration and mutable Jedis-/LettuceClientConfiguration to encapsulate Jedis/Lettuce specific client features. Those should be used for ConnectionFactory configuration.

Additionally we added RedisStandaloneConfiguration next to the existing configurations for Sentinel and Cluster.

Along the way we also removed superfluous test system properties and Gemfire specific hooks and truststore customization.

Original Pull Request: #236
This commit is contained in:
Mark Paluch
2017-01-18 13:48:15 +01:00
committed by Christoph Strobl
parent fef47c54c6
commit a403a530d3
19 changed files with 2773 additions and 228 deletions

View File

@@ -35,7 +35,7 @@ import org.springframework.util.StringUtils;
* Configuration class used for setting up {@link RedisConnection} via {@link RedisConnectionFactory} using connecting
* to <a href="http://redis.io/topics/cluster-spec">Redis Cluster</a>. Useful when setting up a high availability Redis
* environment.
*
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.7
@@ -47,6 +47,7 @@ public class RedisClusterConfiguration {
private Set<RedisNode> clusterNodes;
private Integer maxRedirects;
private String password;
/**
* Creates new {@link RedisClusterConfiguration}.
@@ -57,16 +58,15 @@ public class RedisClusterConfiguration {
/**
* Creates {@link RedisClusterConfiguration} for given hostPort combinations.
*
*
* <pre>
* <code>
* clusterHostAndPorts[0] = 127.0.0.1:23679
* clusterHostAndPorts[1] = 127.0.0.1:23680
* ...
*
* clusterHostAndPorts[1] = 127.0.0.1:23680 ...
* </code>
* <pre>
*
* @param cluster must not be
* {@literal null}.
*
* @param clusterNodes must not be {@literal null}.
*/
public RedisClusterConfiguration(Collection<String> clusterNodes) {
this(new MapPropertySource("RedisClusterConfiguration", asMap(clusterNodes, -1, -1, null)));
@@ -74,7 +74,7 @@ public class RedisClusterConfiguration {
/**
* Creates {@link RedisClusterConfiguration} looking up values in given {@link PropertySource}.
*
*
* <pre>
* <code>
* spring.redis.cluster.nodes=127.0.0.1:23679,127.0.0.1:23680,127.0.0.1:23681
@@ -83,7 +83,7 @@ public class RedisClusterConfiguration {
* spring.redis.cluster.password=foobar
* </code>
* </pre>
*
*
* @param propertySource must not be {@literal null}.
*/
public RedisClusterConfiguration(PropertySource<?> propertySource) {
@@ -104,7 +104,7 @@ public class RedisClusterConfiguration {
/**
* Set {@literal cluster nodes} to connect to.
*
*
* @param nodes must not be {@literal null}.
*/
public void setClusterNodes(Iterable<RedisNode> nodes) {
@@ -120,7 +120,7 @@ public class RedisClusterConfiguration {
/**
* Returns an {@link Collections#unmodifiableSet(Set)} of {@literal cluster nodes}.
*
*
* @return {@link Set} of nodes. Never {@literal null}.
*/
public Set<RedisNode> getClusterNodes() {
@@ -129,7 +129,7 @@ public class RedisClusterConfiguration {
/**
* Add a cluster node to configuration.
*
*
* @param node must not be {@literal null}.
*/
public void addClusterNode(RedisNode node) {
@@ -179,6 +179,22 @@ public class RedisClusterConfiguration {
}
}
/**
* @return
* @since 2.0
*/
public String getPassword() {
return password;
}
/**
* @param password
* @since 2.0
*/
public void setPassword(String password) {
this.password = password;
}
private RedisNode readHostAndPortFromString(String hostAndPort) {
String[] args = split(hostAndPort, ":");

View File

@@ -16,6 +16,7 @@
package org.springframework.data.redis.connection;
import static org.springframework.util.Assert.*;
import static org.springframework.util.Assert.hasText;
import static org.springframework.util.StringUtils.*;
import java.util.Collections;
@@ -26,6 +27,7 @@ import java.util.Set;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@@ -35,6 +37,7 @@ import org.springframework.util.StringUtils;
*
* @author Christoph Strobl
* @author Thomas Darimont
* @author Mark Paluch
* @since 1.4
*/
public class RedisSentinelConfiguration {
@@ -44,6 +47,8 @@ public class RedisSentinelConfiguration {
private NamedNode master;
private Set<RedisNode> sentinels;
private int database;
private String password;
/**
* Creates new {@link RedisSentinelConfiguration}.
@@ -56,9 +61,7 @@ public class RedisSentinelConfiguration {
* Creates {@link RedisSentinelConfiguration} for given hostPort combinations.
*
* <pre>
* sentinelHostAndPorts[0] = 127.0.0.1:23679
* sentinelHostAndPorts[1] = 127.0.0.1:23680
* ...
* sentinelHostAndPorts[0] = 127.0.0.1:23679 sentinelHostAndPorts[1] = 127.0.0.1:23680 ...
*
* <pre>
*
@@ -93,8 +96,8 @@ public class RedisSentinelConfiguration {
}
if (propertySource.containsProperty(REDIS_SENTINEL_NODES_CONFIG_PROPERTY)) {
appendSentinels(commaDelimitedListToSet(propertySource.getProperty(REDIS_SENTINEL_NODES_CONFIG_PROPERTY)
.toString()));
appendSentinels(
commaDelimitedListToSet(propertySource.getProperty(REDIS_SENTINEL_NODES_CONFIG_PROPERTY).toString()));
}
}
@@ -218,6 +221,43 @@ public class RedisSentinelConfiguration {
}
}
/**
* @return
* @since 2.0
*/
public int getDatabase() {
return database;
}
/**
* Sets the index of the database used by this connection factory. Default is 0.
*
* @param index database index.
* @since 2.0
*/
public void setDatabase(int index) {
Assert.isTrue(index >= 0, "invalid DB index (a positive index required)");
this.database = index;
}
/**
* @return
* @since 2.0
*/
public String getPassword() {
return password;
}
/**
* @param password
* @since 2.0
*/
public void setPassword(String password) {
this.password = password;
}
private RedisNode readHostAndPortFromString(String hostAndPort) {
String[] args = split(hostAndPort, ":");

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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;
import org.springframework.util.Assert;
/**
* Configuration class used for setting up {@link RedisConnection} via {@link RedisConnectionFactory} using connecting
* to <a href="http://redis.io/">Redis</a>.
*
* @author Mark Paluch
* @since 2.0
*/
public class RedisStandaloneConfiguration {
public static final String DEFAULT_HOST = "localhost";
public static final int DEFAULT_PORT = 6379;
private String hostName = DEFAULT_HOST;
private int port = DEFAULT_PORT;
private int database;
private String password;
/**
* Create a new default {@link RedisStandaloneConfiguration}.
*/
public RedisStandaloneConfiguration() {}
/**
* Create a new {@link RedisStandaloneConfiguration} given {@code hostName}.
*
* @param hostName must not be {@literal null} or empty.
*/
public RedisStandaloneConfiguration(String hostName) {
this(hostName, DEFAULT_PORT);
}
/**
* Create a new {@link RedisStandaloneConfiguration} given {@code hostName} and {@code port}.
*
* @param hostName must not be {@literal null} or empty.
* @param port a valid TCP port (1-65535).
*/
public RedisStandaloneConfiguration(String hostName, int port) {
Assert.hasText(hostName, "Host name must not be null or empty!");
Assert.isTrue(port >= 1 && port <= 65535,
() -> String.format("Port %d must be a valid TCP port in the range between 1-65535!", port));
this.hostName = hostName;
this.port = port;
}
/**
* @return
*/
public String getHostName() {
return hostName;
}
/**
* @return
*/
public int getPort() {
return port;
}
/**
* @param hostName
*/
public void setHostName(String hostName) {
this.hostName = hostName;
}
/**
* @param port
*/
public void setPort(int port) {
this.port = port;
}
/**
* @return
*/
public int getDatabase() {
return database;
}
/**
* Sets the index of the database used by this connection factory. Default is 0.
*
* @param index database index.
*/
public void setDatabase(int index) {
Assert.isTrue(index >= 0, "invalid DB index (a positive index required)");
this.database = index;
}
/**
* @return
*/
public String getPassword() {
return password;
}
/**
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.jedis;
import java.time.Duration;
import java.util.Optional;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
/**
* Default implementation of {@literal JedisClientConfiguration}.
*
* @author Mark Paluch
* @since 2.0
*/
class DefaultJedisClientConfiguration implements JedisClientConfiguration {
private final boolean useSsl;
private final Optional<SSLSocketFactory> sslSocketFactory;
private final Optional<SSLParameters> sslParameters;
private final Optional<HostnameVerifier> hostnameVerifier;
private final boolean usePooling;
private final Optional<GenericObjectPoolConfig> poolConfig;
private final Optional<String> clientName;
private final Duration readTimeout;
private final Duration connectTimeout;
DefaultJedisClientConfiguration(boolean useSsl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
HostnameVerifier hostnameVerifier, boolean usePooling, GenericObjectPoolConfig poolConfig, String clientName,
Duration readTimeout, Duration connectTimeout) {
this.useSsl = useSsl;
this.sslSocketFactory = Optional.ofNullable(sslSocketFactory);
this.sslParameters = Optional.ofNullable(sslParameters);
this.hostnameVerifier = Optional.ofNullable(hostnameVerifier);
this.usePooling = usePooling;
this.poolConfig = Optional.ofNullable(poolConfig);
this.clientName = Optional.ofNullable(clientName);
this.readTimeout = readTimeout;
this.connectTimeout = connectTimeout;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#useSsl()
*/
@Override
public boolean useSsl() {
return useSsl;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getSslSocketFactory()
*/
@Override
public Optional<SSLSocketFactory> getSslSocketFactory() {
return sslSocketFactory;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getSslParameters()
*/
@Override
public Optional<SSLParameters> getSslParameters() {
return sslParameters;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getHostnameVerifier()
*/
@Override
public Optional<HostnameVerifier> getHostnameVerifier() {
return hostnameVerifier;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#usePooling()
*/
@Override
public boolean usePooling() {
return usePooling;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getPoolConfig()
*/
@Override
public Optional<GenericObjectPoolConfig> getPoolConfig() {
return poolConfig;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getClientName()
*/
@Override
public Optional<String> getClientName() {
return clientName;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getReadTimeout()
*/
@Override
public Duration getReadTimeout() {
return readTimeout;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getConnectTimeout()
*/
@Override
public Duration getConnectTimeout() {
return connectTimeout;
}
}

View File

@@ -0,0 +1,414 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Protocol;
import java.net.SocketAddress;
import java.time.Duration;
import java.util.Optional;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.util.Assert;
/**
* Redis client configuration for jedis. This configuration provides optional configuration elements such as
* {@link SSLSocketFactory} and {@link JedisPoolConfig} specific to jedis client features.
* <p>
* Providing optional elements allows a more specific configuration of the client:
* <ul>
* <li>Whether to use SSL</li>
* <li>Optional {@link SSLSocketFactory}</li>
* <li>Optional {@link SSLParameters}</li>
* <li>Optional {@link HostnameVerifier}</li>
* <li>Whether to use connection-pooling</li>
* <li>Optional {@link GenericObjectPoolConfig}</li>
* <li>Connect {@link Duration timeout}</li>
* <li>Read {@link Duration timeout}</li>
* </ul>
*
* @author Mark Paluch
* @since 2.0
* @see redis.clients.jedis.Jedis
* @see org.springframework.data.redis.connection.RedisStandaloneConfiguration
* @see org.springframework.data.redis.connection.RedisSentinelConfiguration
* @see org.springframework.data.redis.connection.RedisClusterConfiguration
*/
public interface JedisClientConfiguration {
/**
* @return {@literal true} to use SSL, {@literal false} to use unencrypted connections.
*/
boolean useSsl();
/**
* @return the optional {@link SSLSocketFactory}.
*/
Optional<SSLSocketFactory> getSslSocketFactory();
/**
* @return the optional {@link SSLParameters}.
*/
Optional<SSLParameters> getSslParameters();
/**
* @return the optional {@link HostnameVerifier}.
*/
Optional<HostnameVerifier> getHostnameVerifier();
/**
* @return {@literal true} to use connection-pooling.
*/
boolean usePooling();
/**
* @return the optional {@link GenericObjectPoolConfig}.
*/
Optional<GenericObjectPoolConfig> getPoolConfig();
/**
* @return the optional client name to be set with {@code CLIENT SETNAME}.
*/
Optional<String> getClientName();
/**
* @return the connection timeout.
* @see java.net.Socket#connect(SocketAddress, int)
*/
Duration getConnectTimeout();
/**
* @return the read timeout.
* @see java.net.Socket#setSoTimeout(int)
*/
Duration getReadTimeout();
/**
* Creates a new {@link JedisClientConfigurationBuilder} to build {@link JedisClientConfiguration} to be used with the
* jedis client.
*
* @return a new {@link JedisClientConfigurationBuilder} to build {@link JedisClientConfiguration}.
*/
static JedisClientConfigurationBuilder builder() {
return new DefaultJedisClientConfigurationBuilder();
}
/**
* Creates an empty {@link JedisClientConfiguration}.
*
* @return an empty {@link JedisClientConfiguration}.
*/
static JedisClientConfiguration create() {
return builder().build();
}
/**
* Builder for {@link JedisClientConfiguration}.
*/
interface JedisClientConfigurationBuilder {
/**
* Enable SSL connections.
*
* @return {@link JedisSslClientConfigurationBuilder}.
*/
JedisSslClientConfigurationBuilder useSsl();
/**
* Use plaintext connections instead of SSL.
*
* @return {@link JedisSslClientConfigurationBuilder}.
*/
JedisClientConfigurationBuilder usePlaintext();
/**
* Enable connection-pooling.
*
* @return {@link JedisPoolingClientConfigurationBuilder}.
*/
JedisPoolingClientConfigurationBuilder usePooling();
/**
* Disable connection-pooling.
*
* @return {@link JedisClientConfigurationBuilder}.
*/
JedisClientConfigurationBuilder useUnpooledConnections();
/**
* Configure a {@code clientName} to be set with {@code CLIENT SETNAME}.
*
* @param clientName must not be {@literal null}.
* @return {@literal this} builder.
*/
JedisClientConfigurationBuilder clientName(String clientName);
/**
* Configure a read timeout.
*
* @param readTimeout must not be {@literal null}.
* @return {@literal this} builder.
*/
JedisClientConfigurationBuilder readTimeout(Duration readTimeout);
/**
* Configure a connection timeout.
*
* @param connectTimeout must not be {@literal null}.
* @return {@literal this} builder.
*/
JedisClientConfigurationBuilder connectTimeout(Duration connectTimeout);
/**
* Build the {@link JedisClientConfiguration} with the configuration applied from this builder.
*
* @return a new {@link JedisClientConfiguration} object.
*/
JedisClientConfiguration build();
}
/**
* Builder for Pooling-related {@link JedisClientConfiguration}.
*/
interface JedisPoolingClientConfigurationBuilder {
/**
* @param poolConfig must not be {@literal null}.
* @return {@literal this} builder.
*/
JedisPoolingClientConfigurationBuilder poolConfig(GenericObjectPoolConfig poolConfig);
/**
* Return to {@link JedisClientConfigurationBuilder}.
*
* @return {@link JedisClientConfigurationBuilder}.
*/
JedisClientConfigurationBuilder and();
/**
* Build the {@link JedisClientConfiguration} with the configuration applied from this builder.
*
* @return a new {@link JedisClientConfiguration} object.
*/
JedisClientConfiguration build();
}
/**
* Builder for SSL-related {@link JedisClientConfiguration}.
*/
interface JedisSslClientConfigurationBuilder {
/**
* @param sslSocketFactory must not be {@literal null}.
* @return {@literal this} builder.
*/
JedisSslClientConfigurationBuilder sslSocketFactory(SSLSocketFactory sslSocketFactory);
/**
* @param sslParameters must not be {@literal null}.
* @return {@literal this} builder.
*/
JedisSslClientConfigurationBuilder sslParameters(SSLParameters sslParameters);
/**
* @param hostnameVerifier must not be {@literal null}.
* @return {@literal this} builder.
*/
JedisSslClientConfigurationBuilder hostnameVerifier(HostnameVerifier hostnameVerifier);
/**
* Return to {@link JedisClientConfigurationBuilder}.
*
* @return {@link JedisClientConfigurationBuilder}.
*/
JedisClientConfigurationBuilder and();
/**
* Build the {@link JedisClientConfiguration} with the configuration applied from this builder.
*
* @return a new {@link JedisClientConfiguration} object.
*/
JedisClientConfiguration build();
}
/**
* Default {@link JedisClientConfigurationBuilder} implementation to build an immutable
* {@link JedisClientConfiguration}.
*/
class DefaultJedisClientConfigurationBuilder implements JedisClientConfigurationBuilder,
JedisPoolingClientConfigurationBuilder, JedisSslClientConfigurationBuilder {
private boolean useSsl;
private SSLSocketFactory sslSocketFactory;
private SSLParameters sslParameters;
private HostnameVerifier hostnameVerifier;
private boolean usePooling;
private GenericObjectPoolConfig poolConfig = new JedisPoolConfig();
private String clientName;
private Duration readTimeout = Duration.ofMillis(Protocol.DEFAULT_TIMEOUT);
private Duration connectTimeout = Duration.ofMillis(Protocol.DEFAULT_TIMEOUT);
private DefaultJedisClientConfigurationBuilder() {}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder#useSsl()
*/
@Override
public DefaultJedisClientConfigurationBuilder useSsl() {
this.useSsl = true;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder#usePlaintext()
*/
@Override
public JedisClientConfigurationBuilder usePlaintext() {
this.useSsl = false;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisSslClientConfigurationBuilder#sslSocketFactory(javax.net.ssl.SSLSocketFactory)
*/
@Override
public DefaultJedisClientConfigurationBuilder sslSocketFactory(SSLSocketFactory sslSocketFactory) {
Assert.notNull(sslSocketFactory, "SSLSocketFactory must not be null!");
this.sslSocketFactory = sslSocketFactory;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisSslClientConfigurationBuilder#sslParameters(javax.net.ssl.SSLParameters)
*/
@Override
public DefaultJedisClientConfigurationBuilder sslParameters(SSLParameters sslParameters) {
Assert.notNull(sslParameters, "SSLParameters must not be null!");
this.sslParameters = sslParameters;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisSslClientConfigurationBuilder#hostnameVerifier(javax.net.ssl.HostnameVerifier)
*/
@Override
public DefaultJedisClientConfigurationBuilder hostnameVerifier(HostnameVerifier hostnameVerifier) {
Assert.notNull(hostnameVerifier, "HostnameVerifier must not be null!");
this.hostnameVerifier = hostnameVerifier;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder#usePooling()
*/
@Override
public DefaultJedisClientConfigurationBuilder usePooling() {
this.usePooling = true;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder#useUnpooledConnections()
*/
@Override
public JedisClientConfigurationBuilder useUnpooledConnections() {
this.usePooling = false;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisPoolingClientConfigurationBuilder#poolConfig(org.apache.commons.pool2.impl.GenericObjectPoolConfig)
*/
@Override
public DefaultJedisClientConfigurationBuilder poolConfig(GenericObjectPoolConfig poolConfig) {
Assert.notNull(poolConfig, "GenericObjectPoolConfig must not be null!");
this.poolConfig = poolConfig;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisPoolingClientConfigurationBuilder#and()
*/
@Override
public JedisClientConfigurationBuilder and() {
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder#clientName(java.lang.String)
*/
@Override
public JedisClientConfigurationBuilder clientName(String clientName) {
Assert.hasText(clientName, "Client name must not be null or empty!");
this.clientName = clientName;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder#readTimeout(java.time.Duration)
*/
@Override
public JedisClientConfigurationBuilder readTimeout(Duration readTimeout) {
Assert.notNull(readTimeout, "Duration must not be null!");
this.readTimeout = readTimeout;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder#connectTimeout(java.time.Duration)
*/
@Override
public JedisClientConfigurationBuilder connectTimeout(Duration connectTimeout) {
Assert.notNull(connectTimeout, "Duration must not be null!");
this.connectTimeout = connectTimeout;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder#build()
*/
@Override
public JedisClientConfiguration build() {
return new DefaultJedisClientConfiguration(useSsl, sslSocketFactory, sslParameters, hostnameVerifier, usePooling,
poolConfig, clientName, readTimeout, connectTimeout);
}
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.jedis;
import redis.clients.jedis.Client;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
@@ -26,12 +27,18 @@ import redis.clients.jedis.Protocol;
import redis.clients.util.Pool;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
@@ -51,19 +58,31 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisSentinelConnection;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Connection factory creating <a href="http://github.com/xetorthio/jedis">Jedis</a> based connections.
* <p>
* {@link JedisConnectionFactory} should be configured using an environmental configuration and the
* {@link JedisClientConfiguration client configuration}. Jedis supports the following environmental configurations:
* <ul>
* <li>{@link RedisStandaloneConfiguration}</li>
* <li>{@link RedisSentinelConfiguration}</li>
* <li>{@link RedisClusterConfiguration}</li>
* </ul>
*
* @author Costin Leau
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
* @author Fu Jian
* @see JedisClientConfiguration
* @see Jedis
*/
public class JedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory {
@@ -72,7 +91,6 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
JedisConverters.exceptionConverter());
private static final Method SET_TIMEOUT_METHOD;
private static final Method GET_TIMEOUT_METHOD;
static {
@@ -83,27 +101,15 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
setTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "setSoTimeout", int.class);
}
SET_TIMEOUT_METHOD = setTimeoutMethodCandidate;
Method getTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "getTimeout");
if (getTimeoutMethodCandidate == null) {
getTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "getSoTimeout");
}
GET_TIMEOUT_METHOD = getTimeoutMethodCandidate;
}
private final JedisClientConfiguration clientConfiguration;
private JedisShardInfo shardInfo;
private String hostName = "localhost";
private int port = Protocol.DEFAULT_PORT;
private int timeout = Protocol.DEFAULT_TIMEOUT;
private String password;
private boolean usePool = true;
private boolean useSsl = false;
private boolean providedShardInfo = false;
private Pool<Jedis> pool;
private JedisPoolConfig poolConfig = new JedisPoolConfig();
private int dbIndex = 0;
private String clientName;
private boolean convertPipelineAndTxResults = true;
private RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration("localhost",
Protocol.DEFAULT_PORT);
private RedisSentinelConfiguration sentinelConfig;
private RedisClusterConfiguration clusterConfig;
private JedisCluster cluster;
@@ -113,16 +119,38 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* Constructs a new <code>JedisConnectionFactory</code> instance with default settings (default connection pooling, no
* shard information).
*/
public JedisConnectionFactory() {}
public JedisConnectionFactory() {
this(new MutableJedisClientConfiguration());
}
/**
* Constructs a new {@link JedisConnectionFactory} instance given {@link JedisClientConfiguration}.
*
* @param clientConfig must not be {@literal null}
* @since 2.0
*/
private JedisConnectionFactory(JedisClientConfiguration clientConfig) {
Assert.notNull(clientConfig, "JedisClientConfiguration must not be null!");
this.clientConfiguration = clientConfig;
}
/**
* Constructs a new <code>JedisConnectionFactory</code> instance. Will override the other connection parameters passed
* to the factory.
*
* @param shardInfo shard information
* @deprecated since 2.0, configure Jedis with {@link JedisClientConfiguration} and
* {@link RedisStandaloneConfiguration}.
*/
@Deprecated
public JedisConnectionFactory(JedisShardInfo shardInfo) {
this(MutableJedisClientConfiguration.create(shardInfo));
this.shardInfo = shardInfo;
this.providedShardInfo = true;
}
/**
@@ -138,11 +166,11 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* Constructs a new {@link JedisConnectionFactory} instance using the given {@link JedisPoolConfig} applied to
* {@link JedisSentinelPool}.
*
* @param sentinelConfig
* @param sentinelConfig must not be {@literal null}.
* @since 1.4
*/
public JedisConnectionFactory(RedisSentinelConfiguration sentinelConfig) {
this(sentinelConfig, null);
this(sentinelConfig, new MutableJedisClientConfiguration());
}
/**
@@ -154,31 +182,97 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @since 1.4
*/
public JedisConnectionFactory(RedisSentinelConfiguration sentinelConfig, JedisPoolConfig poolConfig) {
this.sentinelConfig = sentinelConfig;
this.poolConfig = poolConfig != null ? poolConfig : new JedisPoolConfig();
this.clientConfiguration = MutableJedisClientConfiguration
.create(poolConfig != null ? poolConfig : new JedisPoolConfig());
}
/**
* Constructs a new {@link JedisConnectionFactory} instance using the given {@link RedisClusterConfiguration} applied
* to create a {@link JedisCluster}.
*
* @param clusterConfig
* @param clusterConfig must not be {@literal null}.
* @since 1.7
*/
public JedisConnectionFactory(RedisClusterConfiguration clusterConfig) {
this.clusterConfig = clusterConfig;
this(clusterConfig, new MutableJedisClientConfiguration());
}
/**
* Constructs a new {@link JedisConnectionFactory} instance using the given {@link RedisClusterConfiguration} applied
* to create a {@link JedisCluster}.
*
* @param clusterConfig
* @param clusterConfig must not be {@literal null}.
* @since 1.7
*/
public JedisConnectionFactory(RedisClusterConfiguration clusterConfig, JedisPoolConfig poolConfig) {
Assert.notNull(clusterConfig, "RedisClusterConfiguration must not be null!");
this.clusterConfig = clusterConfig;
this.clientConfiguration = MutableJedisClientConfiguration.create(poolConfig);
}
/**
* Constructs a new {@link JedisConnectionFactory} instance using the given {@link RedisStandaloneConfiguration}.
*
* @param standaloneConfig must not be {@literal null}.
* @since 2.0
*/
public JedisConnectionFactory(RedisStandaloneConfiguration standaloneConfig) {
this(standaloneConfig, new MutableJedisClientConfiguration());
}
/**
* Constructs a new {@link JedisConnectionFactory} instance using the given {@link RedisStandaloneConfiguration} and
* {@link JedisClientConfiguration}.
*
* @param standaloneConfig must not be {@literal null}.
* @param clientConfig must not be {@literal null}.
* @since 2.0
*/
public JedisConnectionFactory(RedisStandaloneConfiguration standaloneConfig, JedisClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(standaloneConfig, "RedisStandaloneConfiguration must not be null!");
this.standaloneConfig = standaloneConfig;
}
/**
* Constructs a new {@link JedisConnectionFactory} instance using the given {@link RedisSentinelConfiguration} and
* {@link JedisClientConfiguration}.
*
* @param sentinelConfig must not be {@literal null}.
* @param clientConfig must not be {@literal null}.
* @since 2.0
*/
public JedisConnectionFactory(RedisSentinelConfiguration sentinelConfig, JedisClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(sentinelConfig, "RedisSentinelConfiguration must not be null!");
this.sentinelConfig = sentinelConfig;
}
/**
* Constructs a new {@link JedisConnectionFactory} instance using the given {@link RedisClusterConfiguration} and
* {@link JedisClientConfiguration}.
*
* @param clusterConfig must not be {@literal null}.
* @param clientConfig must not be {@literal null}.
* @since 2.0
*/
public JedisConnectionFactory(RedisClusterConfiguration clusterConfig, JedisClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(clusterConfig, "RedisClusterConfiguration must not be null!");
this.clusterConfig = clusterConfig;
this.poolConfig = poolConfig;
}
/**
@@ -190,11 +284,11 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
protected Jedis fetchJedisConnector() {
try {
if (usePool && pool != null) {
if (getUsePool() && pool != null) {
return pool.getResource();
}
Jedis jedis = new Jedis(getShardInfo());
Jedis jedis = createJedis();
// force initialization (see Jedis issue #82)
jedis.connect();
@@ -205,6 +299,25 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
}
}
private Jedis createJedis() {
if (providedShardInfo) {
return new Jedis(getShardInfo());
}
Jedis jedis = new Jedis(getHostName(), getPort(), getConnectTimeout(), getReadTimeout(), isUseSsl(),
clientConfiguration.getSslSocketFactory().orElse(null), //
clientConfiguration.getSslParameters().orElse(null), //
clientConfiguration.getHostnameVerifier().orElse(null));
Client client = jedis.getClient();
client.setPassword(getPassword());
client.setDb(getDatabase());
return jedis;
}
/**
* Post process a newly retrieved connection. Useful for decorating or executing initialization commands on a new
* connection. This implementation simply returns the connection.
@@ -221,23 +334,33 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() {
if (shardInfo == null) {
shardInfo = new JedisShardInfo(hostName, port);
if (StringUtils.hasLength(password)) {
shardInfo.setPassword(password);
if (shardInfo == null && clientConfiguration instanceof MutableJedisClientConfiguration) {
providedShardInfo = false;
shardInfo = new JedisShardInfo(getHostName(), getPort(), isUseSsl(), //
clientConfiguration.getSslSocketFactory().orElse(null), //
clientConfiguration.getSslParameters().orElse(null), //
clientConfiguration.getHostnameVerifier().orElse(null));
if (StringUtils.hasLength(getPassword())) {
shardInfo.setPassword(getPassword());
}
if (timeout > 0) {
setTimeoutOn(shardInfo, timeout);
int readTimeout = getReadTimeout();
if (readTimeout > 0) {
setTimeoutOn(shardInfo, readTimeout);
}
getMutableConfiguration().setShardInfo(shardInfo);
}
if (usePool && clusterConfig == null) {
if (getUsePool() && !isRedisClusterAware()) {
this.pool = createPool();
}
if (clusterConfig != null) {
if (isRedisClusterAware()) {
this.cluster = createCluster();
}
}
@@ -258,9 +381,10 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @since 1.4
*/
protected Pool<Jedis> createRedisSentinelPool(RedisSentinelConfiguration config) {
GenericObjectPoolConfig poolConfig = getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig();
return new JedisSentinelPool(config.getMaster().getName(), convertToJedisSentinelSet(config.getSentinels()),
getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig(), getTimeoutFrom(getShardInfo()),
getShardInfo().getPassword(), Protocol.DEFAULT_DATABASE, clientName);
poolConfig, getConnectTimeout(), getReadTimeout(), getPassword(), Protocol.DEFAULT_DATABASE, getClientName());
}
/**
@@ -271,13 +395,16 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
*/
protected Pool<Jedis> createRedisPool() {
return new JedisPool(getPoolConfig(), getShardInfo().getHost(), getShardInfo().getPort(),
getTimeoutFrom(getShardInfo()), getShardInfo().getPassword(), Protocol.DEFAULT_DATABASE, clientName, useSsl);
return new JedisPool(getPoolConfig(), getHostName(), getPort(), getConnectTimeout(), getReadTimeout(),
getPassword(), Protocol.DEFAULT_DATABASE, getClientName(), isUseSsl(),
clientConfiguration.getSslSocketFactory().orElse(null), //
clientConfiguration.getSslParameters().orElse(null), //
clientConfiguration.getHostnameVerifier().orElse(null));
}
private JedisCluster createCluster() {
JedisCluster cluster = createCluster(this.clusterConfig, this.poolConfig);
JedisCluster cluster = createCluster(this.clusterConfig, getPoolConfig());
this.clusterCommandExecutor = new ClusterCommandExecutor(
new JedisClusterConnection.JedisClusterTopologyProvider(cluster),
new JedisClusterConnection.JedisClusterNodeResourceProvider(cluster), EXCEPTION_TRANSLATION);
@@ -301,11 +428,14 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
hostAndPort.add(new HostAndPort(node.getHost(), node.getPort()));
}
int redirects = clusterConfig.getMaxRedirects() != null ? clusterConfig.getMaxRedirects().intValue() : 5;
int redirects = clusterConfig.getMaxRedirects() != null ? clusterConfig.getMaxRedirects() : 5;
int connectTimeout = getConnectTimeout();
int readTimeout = getReadTimeout();
return StringUtils.hasText(getPassword())
? new JedisCluster(hostAndPort, timeout, timeout, redirects, password, poolConfig)
: new JedisCluster(hostAndPort, timeout, redirects, poolConfig);
? new JedisCluster(hostAndPort, connectTimeout, readTimeout, redirects, getPassword(), poolConfig)
: new JedisCluster(hostAndPort, connectTimeout, readTimeout, redirects, poolConfig);
}
/*
@@ -313,7 +443,9 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() {
if (usePool && pool != null) {
if (getUsePool() && pool != null) {
try {
pool.destroy();
} catch (Exception ex) {
@@ -321,12 +453,15 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
}
pool = null;
}
if (cluster != null) {
try {
cluster.close();
} catch (Exception ex) {
log.warn("Cannot properly close Jedis cluster", ex);
}
try {
clusterCommandExecutor.destroy();
} catch (Exception ex) {
@@ -341,13 +476,14 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
*/
public RedisConnection getConnection() {
if (cluster != null) {
if (isRedisClusterAware()) {
return getClusterConnection();
}
Jedis jedis = fetchJedisConnector();
JedisConnection connection = (usePool ? new JedisConnection(jedis, pool, dbIndex, clientName)
: new JedisConnection(jedis, null, dbIndex, clientName));
String clientName = clientConfiguration.getClientName().orElse(null);
JedisConnection connection = (getUsePool() ? new JedisConnection(jedis, pool, getDatabase(), clientName)
: new JedisConnection(jedis, null, getDatabase(), clientName));
connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults);
return postProcessConnection(connection);
}
@@ -359,7 +495,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
@Override
public RedisClusterConnection getClusterConnection() {
if (cluster == null) {
if (!isRedisClusterAware()) {
throw new InvalidDataAccessApiUsageException("Cluster is not configured!");
}
return new JedisClusterConnection(cluster, clusterCommandExecutor);
@@ -374,31 +510,23 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
}
/**
* Returns the Redis hostName.
* Returns the Redis hostname.
*
* @return the hostName.
*/
public String getHostName() {
return hostName;
return standaloneConfig.getHostName();
}
/**
* Sets the Redis hostName.
* Sets the Redis hostname.
*
* @param hostName the hostName to set.
* @param hostName the hostname to set.
* @deprecated since 2.0, configure the hostname using {@link RedisStandaloneConfiguration}.
*/
@Deprecated
public void setHostName(String hostName) {
this.hostName = hostName;
}
/**
* Sets whether to use SSL.
*
* @param useSsl {@literal true} to use SSL.
* @since 1.8
*/
public void setUseSsl(boolean useSsl) {
this.useSsl = useSsl;
standaloneConfig.setHostName(hostName);
}
/**
@@ -408,7 +536,20 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @since 1.8
*/
public boolean isUseSsl() {
return useSsl;
return clientConfiguration.useSsl();
}
/**
* Sets whether to use SSL.
*
* @param useSsl {@literal true} to use SSL.
* @since 1.8
* @deprecated since 2.0, configure the SSL usage with {@link JedisClientConfiguration}.
* @throws IllegalStateException if {@link JedisClientConfiguration} is immutable.
*/
@Deprecated
public void setUseSsl(boolean useSsl) {
getMutableConfiguration().setUseSsl(useSsl);
}
/**
@@ -417,16 +558,39 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @return password for authentication.
*/
public String getPassword() {
return password;
if (isRedisSentinelAware()) {
return sentinelConfig.getPassword();
}
if (isRedisClusterAware()) {
return clusterConfig.getPassword();
}
return standaloneConfig.getPassword();
}
/**
* Sets the password used for authenticating with the Redis server.
*
* @param password the password to set.
* @deprecated since 2.0, configure the password using {@link RedisStandaloneConfiguration},
* {@link RedisSentinelConfiguration} or {@link RedisClusterConfiguration}.
*/
@Deprecated
public void setPassword(String password) {
this.password = password;
if (isRedisSentinelAware()) {
sentinelConfig.setPassword(password);
return;
}
if (isRedisClusterAware()) {
clusterConfig.setPassword(password);
return;
}
standaloneConfig.setPassword(password);
}
/**
@@ -435,23 +599,27 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @return the Redis port.
*/
public int getPort() {
return port;
return standaloneConfig.getPort();
}
/**
* Sets the port used to connect to the Redis instance.
*
* @param port the Redis port.
* @deprecated since 2.0, configure the port using {@link RedisStandaloneConfiguration}.
*/
@Deprecated
public void setPort(int port) {
this.port = port;
standaloneConfig.setPort(port);
}
/**
* Returns the shardInfo.
*
* @return the shardInfo.
* @deprecated since 2.0.
*/
@Deprecated
public JedisShardInfo getShardInfo() {
return shardInfo;
}
@@ -460,9 +628,16 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* Sets the shard info for this factory.
*
* @param shardInfo the shardInfo to set.
* @deprecated since 2.0, configure the individual properties from {@link JedisShardInfo} using
* {@link JedisClientConfiguration}.
* @throws IllegalStateException if {@link JedisClientConfiguration} is immutable.
*/
@Deprecated
public void setShardInfo(JedisShardInfo shardInfo) {
this.shardInfo = shardInfo;
this.providedShardInfo = true;
getMutableConfiguration().setShardInfo(shardInfo);
}
/**
@@ -471,16 +646,21 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @return the timeout.
*/
public int getTimeout() {
return timeout;
return getReadTimeout();
}
/**
* Sets the timeout.
*
* @param timeout the timeout to set.
* @deprecated since 2.0, configure the timeout using {@link JedisClientConfiguration}.
* @throws IllegalStateException if {@link JedisClientConfiguration} is immutable.
*/
@Deprecated
public void setTimeout(int timeout) {
this.timeout = timeout;
getMutableConfiguration().setReadTimeout(Duration.ofMillis(timeout));
getMutableConfiguration().setConnectTimeout(Duration.ofMillis(timeout));
}
/**
@@ -489,16 +669,19 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @return the use of connection pooling.
*/
public boolean getUsePool() {
return usePool;
return clientConfiguration.usePooling();
}
/**
* Turns on or off the use of connection pooling.
*
* @param usePool the usePool to set.
* @deprecated since 2.0, configure pooling usage with {@link JedisClientConfiguration}.
* @throws IllegalStateException if {@link JedisClientConfiguration} is immutable.
*/
@Deprecated
public void setUsePool(boolean usePool) {
this.usePool = usePool;
getMutableConfiguration().setUsePooling(usePool);
}
/**
@@ -506,17 +689,20 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
*
* @return the poolConfig
*/
public JedisPoolConfig getPoolConfig() {
return poolConfig;
public GenericObjectPoolConfig getPoolConfig() {
return clientConfiguration.getPoolConfig().orElse(null);
}
/**
* Sets the pool configuration for this factory.
*
* @param poolConfig the poolConfig to set.
* @deprecated since 2.0, configure {@link JedisPoolConfig} using {@link JedisClientConfiguration}.
* @throws IllegalStateException if {@link JedisClientConfiguration} is immutable.
*/
@Deprecated
public void setPoolConfig(JedisPoolConfig poolConfig) {
this.poolConfig = poolConfig;
getMutableConfiguration().setPoolConfig(poolConfig);
}
/**
@@ -525,17 +711,32 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @return the database index.
*/
public int getDatabase() {
return dbIndex;
if (isRedisSentinelAware()) {
return sentinelConfig.getDatabase();
}
return standaloneConfig.getDatabase();
}
/**
* Sets the index of the database used by this connection factory. Default is 0.
*
* @param index database index.
* @deprecated since 2.0, configure the client name using {@link RedisSentinelConfiguration} or
* {@link RedisStandaloneConfiguration}.
*/
@Deprecated
public void setDatabase(int index) {
Assert.isTrue(index >= 0, "invalid DB index (a positive index required)");
this.dbIndex = index;
if (isRedisSentinelAware()) {
sentinelConfig.setDatabase(index);
return;
}
standaloneConfig.setDatabase(index);
}
/**
@@ -545,7 +746,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
* @since 1.8
*/
public String getClientName() {
return clientName;
return clientConfiguration.getClientName().orElse(null);
}
/**
@@ -553,9 +754,44 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
*
* @param clientName the client name.
* @since 1.8
* @deprecated since 2.0, configure the client name using {@link JedisClientConfiguration}.
* @throws IllegalStateException if {@link JedisClientConfiguration} is immutable.
*/
@Deprecated
public void setClientName(String clientName) {
this.clientName = clientName;
this.getMutableConfiguration().setClientName(clientName);
}
/**
* @return the {@link JedisClientConfiguration}.
* @since 2.0
*/
public JedisClientConfiguration getClientConfiguration() {
return clientConfiguration;
}
/**
* @return the {@link RedisStandaloneConfiguration}.
* @since 2.0
*/
public RedisStandaloneConfiguration getStandaloneConfiguration() {
return standaloneConfig;
}
/**
* @return the {@link RedisStandaloneConfiguration}, may be {@literal null}.
* @since 2.0
*/
public RedisSentinelConfiguration getSentinelConfiguration() {
return sentinelConfig;
}
/**
* @return the {@link RedisClusterConfiguration}, may be {@literal null}.
* @since 2.0
*/
public RedisClusterConfiguration getClusterConfiguration() {
return clusterConfig;
}
/**
@@ -588,6 +824,14 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
return sentinelConfig != null;
}
/**
* @return true when {@link RedisClusterConfiguration} is present.
* @since 2.0
*/
public boolean isRedisClusterAware() {
return clusterConfig != null;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConnectionFactory#getSentinelConnection()
*/
@@ -607,7 +851,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
for (RedisNode node : this.sentinelConfig.getSentinels()) {
Jedis jedis = new Jedis(node.getHost(), node.getPort());
Jedis jedis = new Jedis(node.getHost(), node.getPort(), getConnectTimeout(), getReadTimeout());
if (jedis.ping().equalsIgnoreCase("pong")) {
@@ -635,18 +879,176 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
}
private void potentiallySetClientName(Jedis jedis) {
if (StringUtils.hasText(clientName)) {
jedis.clientSetname(clientName);
}
clientConfiguration.getClientName().ifPresent(jedis::clientSetname);
}
private void setTimeoutOn(JedisShardInfo shardInfo, int timeout) {
ReflectionUtils.invokeMethod(SET_TIMEOUT_METHOD, shardInfo, timeout);
}
private int getTimeoutFrom(JedisShardInfo shardInfo) {
return (Integer) ReflectionUtils.invokeMethod(GET_TIMEOUT_METHOD, shardInfo);
private int getReadTimeout() {
return Math.toIntExact(clientConfiguration.getReadTimeout().toMillis());
}
private int getConnectTimeout() {
return Math.toIntExact(clientConfiguration.getConnectTimeout().toMillis());
}
private MutableJedisClientConfiguration getMutableConfiguration() {
Assert.state(clientConfiguration instanceof MutableJedisClientConfiguration,
() -> String.format("Client configuration must be instance of MutableJedisClientConfiguration but is %s",
ClassUtils.getShortName(clientConfiguration.getClass())));
return (MutableJedisClientConfiguration) clientConfiguration;
}
static class MutableJedisClientConfiguration implements JedisClientConfiguration {
private boolean useSsl;
private SSLSocketFactory sslSocketFactory;
private SSLParameters sslParameters;
private HostnameVerifier hostnameVerifier;
private boolean usePooling = true;
private GenericObjectPoolConfig poolConfig = new JedisPoolConfig();
private String clientName;
private Duration readTimeout = Duration.ofMillis(Protocol.DEFAULT_TIMEOUT);
private Duration connectTimeout = Duration.ofMillis(Protocol.DEFAULT_TIMEOUT);
public static JedisClientConfiguration create(JedisShardInfo shardInfo) {
MutableJedisClientConfiguration configuration = new MutableJedisClientConfiguration();
configuration.setShardInfo(shardInfo);
return configuration;
}
public static JedisClientConfiguration create(GenericObjectPoolConfig jedisPoolConfig) {
MutableJedisClientConfiguration configuration = new MutableJedisClientConfiguration();
configuration.setPoolConfig(jedisPoolConfig);
return configuration;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#useSsl()
*/
@Override
public boolean useSsl() {
return useSsl;
}
public void setUseSsl(boolean useSsl) {
this.useSsl = useSsl;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getSslSocketFactory()
*/
@Override
public Optional<SSLSocketFactory> getSslSocketFactory() {
return Optional.ofNullable(sslSocketFactory);
}
public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
this.sslSocketFactory = sslSocketFactory;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getSslParameters()
*/
@Override
public Optional<SSLParameters> getSslParameters() {
return Optional.ofNullable(sslParameters);
}
public void setSslParameters(SSLParameters sslParameters) {
this.sslParameters = sslParameters;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getHostnameVerifier()
*/
@Override
public Optional<HostnameVerifier> getHostnameVerifier() {
return Optional.ofNullable(hostnameVerifier);
}
public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#usePooling()
*/
@Override
public boolean usePooling() {
return usePooling;
}
public void setUsePooling(boolean usePooling) {
this.usePooling = usePooling;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getPoolConfig()
*/
@Override
public Optional<GenericObjectPoolConfig> getPoolConfig() {
return Optional.ofNullable(poolConfig);
}
public void setPoolConfig(GenericObjectPoolConfig poolConfig) {
this.poolConfig = poolConfig;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getClientName()
*/
@Override
public Optional<String> getClientName() {
return Optional.ofNullable(clientName);
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getReadTimeout()
*/
@Override
public Duration getReadTimeout() {
return readTimeout;
}
public void setReadTimeout(Duration readTimeout) {
this.readTimeout = readTimeout;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.jedis.JedisClientConfiguration#getConnectTimeout()
*/
@Override
public Duration getConnectTimeout() {
return connectTimeout;
}
public void setConnectTimeout(Duration connectTimeout) {
this.connectTimeout = connectTimeout;
}
public void setShardInfo(JedisShardInfo shardInfo) {
setSslSocketFactory(shardInfo.getSslSocketFactory());
setSslParameters(shardInfo.getSslParameters());
setHostnameVerifier(shardInfo.getHostnameVerifier());
setUseSsl(shardInfo.getSsl());
setConnectTimeout(Duration.ofMillis(shardInfo.getConnectionTimeout()));
setReadTimeout(Duration.ofMillis(shardInfo.getSoTimeout()));
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 io.lettuce.core.ClientOptions;
import io.lettuce.core.resource.ClientResources;
import java.time.Duration;
import java.util.Optional;
/**
* Default implementation of {@literal LettuceClientConfiguration}.
*
* @author Mark Paluch
* @since 2.0
*/
class DefaultLettuceClientConfiguration implements LettuceClientConfiguration {
private final boolean useSsl;
private final boolean verifyPeer;
private final boolean startTls;
private final Optional<ClientResources> clientResources;
private final Optional<ClientOptions> clientOptions;
private final Duration timeout;
private final Duration shutdownTimeout;
DefaultLettuceClientConfiguration(boolean useSsl, boolean verifyPeer, boolean startTls,
ClientResources clientResources, ClientOptions clientOptions, Duration timeout, Duration shutdownTimeout) {
this.useSsl = useSsl;
this.verifyPeer = verifyPeer;
this.startTls = startTls;
this.clientResources = Optional.ofNullable(clientResources);
this.clientOptions = Optional.ofNullable(clientOptions);
this.timeout = timeout;
this.shutdownTimeout = shutdownTimeout;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#useSsl()
*/
@Override
public boolean useSsl() {
return useSsl;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#isVerifyPeer()
*/
@Override
public boolean isVerifyPeer() {
return verifyPeer;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#isStartTls()
*/
@Override
public boolean isStartTls() {
return startTls;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#getClientResources()
*/
@Override
public Optional<ClientResources> getClientResources() {
return clientResources;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#getClientOptions()
*/
@Override
public Optional<ClientOptions> getClientOptions() {
return clientOptions;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#getTimeout()
*/
@Override
public Duration getTimeout() {
return timeout;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#getShutdownTimeout()
*/
@Override
public Duration getShutdownTimeout() {
return shutdownTimeout;
}
}

View File

@@ -0,0 +1,339 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisURI;
import io.lettuce.core.resource.ClientResources;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.springframework.util.Assert;
/**
* Redis client configuration for lettuce. This configuration provides optional configuration elements such as
* {@link ClientResources} and {@link ClientOptions} specific to Lettuce client features.
* <p>
* Providing optional elements allows a more specific configuration of the client:
* <ul>
* <li>Whether to use SSL</li>
* <li>Whether to verify peers using SSL</li>
* <li>Whether to use StartTLS</li>
* <li>Optional {@link ClientResources}</li>
* <li>Optional {@link ClientOptions}</li>
* <li>Client {@link Duration timeout}</li>
* <li>Shutdown {@link Duration timeout}</li>
* </ul>
*
* @author Mark Paluch
* @since 2.0
* @see org.springframework.data.redis.connection.RedisStandaloneConfiguration
* @see org.springframework.data.redis.connection.RedisSentinelConfiguration
* @see org.springframework.data.redis.connection.RedisClusterConfiguration
*/
public interface LettuceClientConfiguration {
/**
* @return {@literal true} to use SSL, {@literal false} to use unencrypted connections.
*/
boolean useSsl();
/**
* @return {@literal true} to verify peers when using {@link #useSsl() SSL}.
*/
boolean isVerifyPeer();
/**
* @return {@literal true} to use Start TLS ({@code true} if the first write request shouldn't be encrypted).
*/
boolean isStartTls();
/**
* @return the optional {@link ClientResources}.
*/
Optional<ClientResources> getClientResources();
/**
* @return the optional {@link io.lettuce.core.ClientOptions}.
*/
Optional<ClientOptions> getClientOptions();
/**
* @return the timeout.
*/
Duration getTimeout();
/**
* @return the shutdown timeout used to close the client.
* @see io.lettuce.core.AbstractRedisClient#shutdown(long, long, TimeUnit)
*/
Duration getShutdownTimeout();
/**
* Creates a new {@link LettuceClientConfigurationBuilder} to build {@link LettuceClientConfiguration} to be used with
* the Lettuce client.
*
* @return a new {@link LettuceClientConfigurationBuilder} to build {@link LettuceClientConfiguration}.
*/
static LettuceClientConfigurationBuilder builder() {
return new DefaultLettuceClientConfigurationBuilder();
}
/**
* Creates an empty {@link LettuceClientConfiguration}.
*
* @return an empty {@link LettuceClientConfiguration}.
*/
static LettuceClientConfiguration create() {
return builder().build();
}
/**
* Builder for {@link LettuceClientConfiguration}.
*/
interface LettuceClientConfigurationBuilder {
/**
* Enable SSL connections.
*
* @return {@link LettuceSslClientConfigurationBuilder}.
*/
LettuceSslClientConfigurationBuilder useSsl();
/**
* Use plaintext connections instead of SSL.
*
* @return {@link LettuceClientConfigurationBuilder}.
*/
LettuceClientConfigurationBuilder usePlaintext();
/**
* Configure {@link ClientResources}.
*
* @param clientResources must not be {@literal null}.
* @return {@literal this} builder.
*/
LettuceClientConfigurationBuilder clientResources(ClientResources clientResources);
/**
* Configure {@link ClientOptions}.
*
* @param clientOptions must not be {@literal null}.
* @return {@literal this} builder.
* @see ClientOptions
* @see io.lettuce.core.cluster.ClusterClientOptions
*/
LettuceClientConfigurationBuilder clientOptions(ClientOptions clientOptions);
/**
* Configure a timeout.
*
* @param timeout must not be {@literal null}.
* @return {@literal this} builder.
*/
LettuceClientConfigurationBuilder timeout(Duration timeout);
/**
* Configure a shutdown timeout.
*
* @param shutdownTimeout must not be {@literal null}.
* @return {@literal this} builder.
*/
LettuceClientConfigurationBuilder shutdownTimeout(Duration shutdownTimeout);
/**
* Build the {@link LettuceClientConfiguration} with the configuration applied from this builder.
*
* @return a new {@link LettuceClientConfiguration} object.
*/
LettuceClientConfiguration build();
}
/**
* Builder for SSL-related {@link LettuceClientConfiguration}.
*/
interface LettuceSslClientConfigurationBuilder {
/**
* Enable peer verification.
*
* @return {@literal this} builder.
*/
LettuceSslClientConfigurationBuilder verifyPeer();
/**
* Enable/disable peer verification.
*
* @param verifyPeer {@literal true} to enable peer verification, {@literal false} to skip peer verification.
* @return {@literal this} builder.
*/
LettuceSslClientConfigurationBuilder verifyPeer(boolean verifyPeer);
/**
* Enable Start TLS to send the first bytes unencrypted.
*
* @return {@literal this} builder.
*/
LettuceSslClientConfigurationBuilder startTls();
/**
* Return to {@link LettuceClientConfigurationBuilder}.
*
* @return {@link LettuceClientConfigurationBuilder}.
*/
LettuceClientConfigurationBuilder and();
/**
* Build the {@link LettuceClientConfiguration} with the configuration applied from this builder.
*
* @return a new {@link LettuceClientConfiguration} object.
*/
LettuceClientConfiguration build();
}
/**
* Default {@link LettuceClientConfigurationBuilder} implementation to build an immutable
* {@link LettuceClientConfiguration}.
*/
class DefaultLettuceClientConfigurationBuilder
implements LettuceClientConfigurationBuilder, LettuceSslClientConfigurationBuilder {
private boolean useSsl;
private boolean verifyPeer = true;
private boolean startTls;
private ClientResources clientResources;
private ClientOptions clientOptions;
private Duration timeout = Duration.ofSeconds(RedisURI.DEFAULT_TIMEOUT);
private Duration shutdownTimeout = Duration.ofSeconds(2);
private DefaultLettuceClientConfigurationBuilder() {}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder#useSsl()
*/
@Override
public LettuceSslClientConfigurationBuilder useSsl() {
this.useSsl = true;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder#usePlaintext()
*/
@Override
public LettuceClientConfigurationBuilder usePlaintext() {
this.useSsl = false;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceSslClientConfigurationBuilder#verifyPeer()
*/
@Override
public LettuceSslClientConfigurationBuilder verifyPeer() {
return verifyPeer(true);
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceSslClientConfigurationBuilder#verifyPeer(boolean)
*/
@Override
public LettuceSslClientConfigurationBuilder verifyPeer(boolean verifyPeer) {
this.verifyPeer = verifyPeer;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceSslClientConfigurationBuilder#startTls()
*/
@Override
public LettuceSslClientConfigurationBuilder startTls() {
this.startTls = true;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceSslClientConfigurationBuilder#and()
*/
@Override
public LettuceClientConfigurationBuilder and() {
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder#clientResources(io.lettuce.core.resource.ClientResources)
*/
@Override
public LettuceClientConfigurationBuilder clientResources(ClientResources clientResources) {
Assert.notNull(clientResources, "ClientResources must not be null!");
this.clientResources = clientResources;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder#clientOptions(io.lettuce.core.ClientOptions)
*/
@Override
public LettuceClientConfigurationBuilder clientOptions(ClientOptions clientOptions) {
Assert.notNull(clientOptions, "ClientOptions must not be null!");
this.clientOptions = clientOptions;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder#timeout(java.time.Duration)
*/
@Override
public LettuceClientConfigurationBuilder timeout(Duration timeout) {
Assert.notNull(timeout, "Duration must not be null!");
this.timeout = timeout;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder#shutdownTimeout(java.time.Duration)
*/
@Override
public LettuceClientConfigurationBuilder shutdownTimeout(Duration shutdownTimeout) {
Assert.notNull(timeout, "Duration must not be null!");
this.shutdownTimeout = shutdownTimeout;
return this;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder#build()
*/
@Override
public LettuceClientConfiguration build() {
return new DefaultLettuceClientConfiguration(useSsl, verifyPeer, startTls, clientResources, clientOptions,
timeout, shutdownTimeout);
}
}
}

View File

@@ -16,15 +16,19 @@
package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.AbstractRedisClient;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisException;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.resource.ClientResources;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
@@ -37,16 +41,7 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.data.redis.ExceptionTranslationStrategy;
import org.springframework.data.redis.PassThroughExceptionTranslationStrategy;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.connection.ClusterCommandExecutor;
import org.springframework.data.redis.connection.Pool;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisClusterConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisSentinelConnection;
import org.springframework.data.redis.connection.*;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -62,6 +57,14 @@ import org.springframework.util.StringUtils;
* a {@link Pool} to pool dedicated connections. If shareNativeConnection is true, the pool will be used to select a
* connection for blocking and tx operations only, which should not share a connection. If native connection sharing is
* disabled, the selected connection will be used for all operations.
* <p>
* {@link LettuceConnectionFactory} should be configured using an environmental configuration and the
* {@link LettuceConnectionFactory client configuration}. Lettuce supports the following environmental configurations:
* <ul>
* <li>{@link RedisStandaloneConfiguration}</li>
* <li>{@link RedisSentinelConfiguration}</li>
* <li>{@link RedisClusterConfiguration}</li>
* </ul>
*
* @author Costin Leau
* @author Jennifer Hickey
@@ -79,67 +82,128 @@ public class LettuceConnectionFactory
LettuceConverters.exceptionConverter());
private final Log log = LogFactory.getLog(getClass());
private final LettuceClientConfiguration clientConfiguration;
private String hostName = "localhost";
private int port = 6379;
private AbstractRedisClient client;
private long timeout = TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS);
private long shutdownTimeout = TimeUnit.MILLISECONDS.convert(2, TimeUnit.SECONDS);
private boolean validateConnection = false;
private boolean shareNativeConnection = true;
private StatefulRedisConnection<byte[], byte[]> connection;
private LettucePool pool;
private int dbIndex = 0;
/** Synchronization monitor for the shared Connection */
private final Object connectionMonitor = new Object();
private String password;
private boolean convertPipelineAndTxResults = true;
private RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration("localhost", 6379);
private RedisSentinelConfiguration sentinelConfiguration;
private RedisClusterConfiguration clusterConfiguration;
private ClusterCommandExecutor clusterCommandExecutor;
private ClientResources clientResources;
private boolean useSsl = false;
private boolean verifyPeer = true;
private boolean startTls = false;
/**
* Constructs a new <code>LettuceConnectionFactory</code> instance with default settings.
* Constructs a new {@link LettuceConnectionFactory} instance with default settings.
*/
public LettuceConnectionFactory() {}
public LettuceConnectionFactory() {
this(new MutableLettuceClientConfiguration());
}
/**
* Constructs a new <code>LettuceConnectionFactory</code> instance with default settings.
* Constructs a new {@link LettuceConnectionFactory} instance given {@link LettuceClientConfiguration}.
*
* @param clientConfig must not be {@literal null}
* @since 2.0
*/
private LettuceConnectionFactory(LettuceClientConfiguration clientConfig) {
Assert.notNull(clientConfig, "LettuceClientConfiguration must not be null!");
this.clientConfiguration = clientConfig;
}
/**
* Constructs a new {@link LettuceConnectionFactory} instance with default settings.
*/
public LettuceConnectionFactory(String host, int port) {
this.hostName = host;
this.port = port;
this(new RedisStandaloneConfiguration(host, port), new MutableLettuceClientConfiguration());
}
/**
* Constructs a new {@link LettuceConnectionFactory} instance using the given {@link RedisSentinelConfiguration}
*
* @param sentinelConfiguration
* @param sentinelConfiguration must not be {@literal null}.
* @since 1.6
*/
public LettuceConnectionFactory(RedisSentinelConfiguration sentinelConfiguration) {
this.sentinelConfiguration = sentinelConfiguration;
this(sentinelConfiguration, new MutableLettuceClientConfiguration());
}
/**
* Constructs a new {@link LettuceConnectionFactory} instance using the given {@link RedisClusterConfiguration}
* applied to create a {@link RedisClusterClient}.
*
* @param clusterConfig
* @param clusterConfiguration must not be {@literal null}.
* @since 1.7
*/
public LettuceConnectionFactory(RedisClusterConfiguration clusterConfig) {
this.clusterConfiguration = clusterConfig;
public LettuceConnectionFactory(RedisClusterConfiguration clusterConfiguration) {
this(clusterConfiguration, new MutableLettuceClientConfiguration());
}
public LettuceConnectionFactory(LettucePool pool) {
this(new MutableLettuceClientConfiguration());
this.pool = pool;
}
/**
* Constructs a new {@link LettuceConnectionFactory} instance using the given {@link RedisStandaloneConfiguration} and
* {@link LettuceClientConfiguration}.
*
* @param standaloneConfig must not be {@literal null}.
* @param clientConfig must not be {@literal null}.
* @since 2.0
*/
public LettuceConnectionFactory(RedisStandaloneConfiguration standaloneConfig,
LettuceClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(standaloneConfig, "RedisStandaloneConfiguration must not be null!");
this.standaloneConfig = standaloneConfig;
}
/**
* Constructs a new {@link LettuceConnectionFactory} instance using the given {@link RedisSentinelConfiguration} and
* {@link LettuceClientConfiguration}.
*
* @param sentinelConfiguration must not be {@literal null}.
* @param clientConfig must not be {@literal null}.
* @since 2.0
*/
public LettuceConnectionFactory(RedisSentinelConfiguration sentinelConfiguration,
LettuceClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(sentinelConfiguration, "RedisSentinelConfiguration must not be null!");
this.sentinelConfiguration = sentinelConfiguration;
}
/**
* Constructs a new {@link LettuceConnectionFactory} instance using the given {@link RedisClusterConfiguration} and
* {@link LettuceClientConfiguration}.
*
* @param clusterConfiguration must not be {@literal null}.
* @param clientConfig must not be {@literal null}.
* @since 2.0
*/
public LettuceConnectionFactory(RedisClusterConfiguration clusterConfiguration,
LettuceClientConfiguration clientConfig) {
this(clientConfig);
Assert.notNull(clusterConfiguration, "RedisClusterConfiguration must not be null!");
this.clusterConfiguration = clusterConfiguration;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
@@ -157,7 +221,8 @@ public class LettuceConnectionFactory
resetConnection();
try {
client.shutdown(shutdownTimeout, shutdownTimeout, TimeUnit.MILLISECONDS);
Duration timeout = clientConfiguration.getShutdownTimeout();
client.shutdown(timeout.toMillis(), timeout.toMillis(), TimeUnit.MILLISECONDS);
} catch (Exception e) {
if (log.isWarnEnabled()) {
@@ -186,7 +251,8 @@ public class LettuceConnectionFactory
return getClusterConnection();
}
LettuceConnection connection = new LettuceConnection(getSharedConnection(), timeout, client, pool, dbIndex);
LettuceConnection connection = new LettuceConnection(getSharedConnection(), getTimeout(), client, pool,
getDatabase());
connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults);
return connection;
}
@@ -283,16 +349,18 @@ public class LettuceConnectionFactory
* @return the host.
*/
public String getHostName() {
return hostName;
return standaloneConfig.getHostName();
}
/**
* Sets the host.
* Sets the hostname.
*
* @param host the host to set.
* @param hostName the hostname to set.
* @deprecated since 2.0, configure the hostname using {@link RedisStandaloneConfiguration}.
*/
public void setHostName(String host) {
this.hostName = host;
@Deprecated
public void setHostName(String hostName) {
standaloneConfig.setHostName(hostName);
}
/**
@@ -301,16 +369,18 @@ public class LettuceConnectionFactory
* @return the port.
*/
public int getPort() {
return port;
return standaloneConfig.getPort();
}
/**
* Sets the port.
*
* @param port the port to set.
* @deprecated since 2.0, configure the port using {@link RedisStandaloneConfiguration}.
*/
@Deprecated
public void setPort(int port) {
this.port = port;
standaloneConfig.setPort(port);
}
/**
@@ -319,25 +389,19 @@ public class LettuceConnectionFactory
* @return connection timeout.
*/
public long getTimeout() {
return timeout;
return getClientTimeout();
}
/**
* Sets the connection timeout (in milliseconds).
*
* @param timeout connection timeout.
* @param timeout the timeout.
* @deprecated since 2.0, configure the timeout using {@link LettuceClientConfiguration}.
* @throws IllegalStateException if {@link LettuceClientConfiguration} is immutable.
*/
@Deprecated
public void setTimeout(long timeout) {
this.timeout = timeout;
}
/**
* Sets to use SSL connection.
*
* @param useSsl {@literal true} to use SSL.
*/
public void setUseSsl(boolean useSsl) {
this.useSsl = useSsl;
getMutableConfiguration().setTimeout(Duration.ofMillis(timeout));
}
/**
@@ -346,25 +410,40 @@ public class LettuceConnectionFactory
* @return use of SSL.
*/
public boolean isUseSsl() {
return useSsl;
return clientConfiguration.useSsl();
}
/**
* Sets to use SSL connection.
*
* @param useSsl {@literal true} to use SSL.
* @deprecated since 2.0, configure SSL usage using {@link LettuceClientConfiguration}.
* @throws IllegalStateException if {@link LettuceClientConfiguration} is immutable.
*/
@Deprecated
public void setUseSsl(boolean useSsl) {
getMutableConfiguration().setUseSsl(useSsl);
}
/**
* Returns whether to verify certificate validity/hostname check when SSL is used.
*
* @return whether to verify peers when using SSL.
*/
public boolean isVerifyPeer() {
return clientConfiguration.isVerifyPeer();
}
/**
* Sets to use verify certificate validity/hostname check when SSL is used.
*
* @param verifyPeer {@literal false} not to verify hostname.
* @deprecated since 2.0, configure peer verification using {@link LettuceClientConfiguration}.
* @throws IllegalStateException if {@link LettuceClientConfiguration} is immutable.
*/
@Deprecated
public void setVerifyPeer(boolean verifyPeer) {
this.verifyPeer = verifyPeer;
}
/**
* Returns whether to verify certificate validity/hostname check when SSL is used.
*
* @return verify peers when using SSL.
*/
public boolean isVerifyPeer() {
return verifyPeer;
getMutableConfiguration().setVerifyPeer(verifyPeer);
}
/**
@@ -373,16 +452,19 @@ public class LettuceConnectionFactory
* @return use of StartTLS.
*/
public boolean isStartTls() {
return startTls;
return clientConfiguration.isStartTls();
}
/**
* Sets to issue StartTLS.
*
* @param startTls {@literal true} to issue StartTLS.
* @deprecated since 2.0, configure StartTLS using {@link LettuceClientConfiguration}.
* @throws IllegalStateException if {@link LettuceClientConfiguration} is immutable.
*/
@Deprecated
public void setStartTls(boolean startTls) {
this.startTls = startTls;
getMutableConfiguration().setStartTls(startTls);
}
/**
@@ -436,7 +518,12 @@ public class LettuceConnectionFactory
* @return the database index.
*/
public int getDatabase() {
return dbIndex;
if (isRedisSentinelAware()) {
return sentinelConfiguration.getDatabase();
}
return standaloneConfig.getDatabase();
}
/**
@@ -445,8 +532,15 @@ public class LettuceConnectionFactory
* @param index database index
*/
public void setDatabase(int index) {
Assert.isTrue(index >= 0, "invalid DB index (a positive index required)");
this.dbIndex = index;
if (isRedisSentinelAware()) {
sentinelConfiguration.setDatabase(index);
return;
}
standaloneConfig.setDatabase(index);
}
/**
@@ -455,16 +549,39 @@ public class LettuceConnectionFactory
* @return password for authentication.
*/
public String getPassword() {
return password;
if (isRedisSentinelAware()) {
return sentinelConfiguration.getPassword();
}
if (isClusterAware()) {
return clusterConfiguration.getPassword();
}
return standaloneConfig.getPassword();
}
/**
* Sets the password used for authenticating with the Redis server.
*
* @param password the password to set
* @deprecated since 2.0, configure the password using {@link RedisStandaloneConfiguration},
* {@link RedisSentinelConfiguration} or {@link RedisClusterConfiguration}.
*/
@Deprecated
public void setPassword(String password) {
this.password = password;
if (isRedisSentinelAware()) {
sentinelConfiguration.setPassword(password);
return;
}
if (isClusterAware()) {
clusterConfiguration.setPassword(password);
return;
}
standaloneConfig.setPassword(password);
}
/**
@@ -474,7 +591,7 @@ public class LettuceConnectionFactory
* @since 1.6
*/
public long getShutdownTimeout() {
return shutdownTimeout;
return clientConfiguration.getShutdownTimeout().toMillis();
}
/**
@@ -482,9 +599,12 @@ public class LettuceConnectionFactory
*
* @param shutdownTimeout the shutdown timeout.
* @since 1.6
* @deprecated since 2.0, configure the shutdown timeout using {@link LettuceClientConfiguration}.
* @throws IllegalStateException if {@link LettuceClientConfiguration} is immutable.
*/
@Deprecated
public void setShutdownTimeout(long shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
getMutableConfiguration().setShutdownTimeout(Duration.ofMillis(shutdownTimeout));
}
/**
@@ -494,7 +614,7 @@ public class LettuceConnectionFactory
* @since 1.7
*/
public ClientResources getClientResources() {
return clientResources;
return clientConfiguration.getClientResources().orElse(null);
}
/**
@@ -503,9 +623,44 @@ public class LettuceConnectionFactory
*
* @param clientResources can be {@literal null}.
* @since 1.7
* @deprecated since 2.0, configure {@link ClientResources} using {@link LettuceClientConfiguration}.
* @throws IllegalStateException if {@link LettuceClientConfiguration} is immutable.
*/
@Deprecated
public void setClientResources(ClientResources clientResources) {
this.clientResources = clientResources;
getMutableConfiguration().setClientResources(clientResources);
}
/**
* @return the {@link LettuceClientConfiguration}.
* @since 2.0
*/
public LettuceClientConfiguration getClientConfiguration() {
return clientConfiguration;
}
/**
* @return the {@link RedisStandaloneConfiguration}.
* @since 2.0
*/
public RedisStandaloneConfiguration getStandaloneConfiguration() {
return standaloneConfig;
}
/**
* @return the {@link RedisStandaloneConfiguration}, may be {@literal null}.
* @since 2.0
*/
public RedisSentinelConfiguration getSentinelConfiguration() {
return sentinelConfiguration;
}
/**
* @return the {@link RedisClusterConfiguration}, may be {@literal null}.
* @since 2.0
*/
public RedisClusterConfiguration getClusterConfiguration() {
return clusterConfiguration;
}
/**
@@ -568,8 +723,8 @@ public class LettuceConnectionFactory
StatefulRedisConnection<byte[], byte[]> connection = null;
if (client instanceof RedisClient) {
connection = ((RedisClient) client).connect(LettuceConnection.CODEC);
if (dbIndex > 0) {
connection.sync().select(dbIndex);
if (getDatabase() > 0) {
connection.sync().select(getDatabase());
}
} else {
connection = null;
@@ -585,11 +740,12 @@ public class LettuceConnectionFactory
if (isRedisSentinelAware()) {
RedisURI redisURI = getSentinelRedisURI();
if (clientResources == null) {
return RedisClient.create(redisURI);
}
RedisClient redisClient = clientConfiguration.getClientResources() //
.map(clientResources -> RedisClient.create(clientResources, redisURI)) //
.orElseGet(() -> RedisClient.create(redisURI));
return RedisClient.create(clientResources, redisURI);
clientConfiguration.getClientOptions().ifPresent(redisClient::setOptions);
return redisClient;
}
if (isClusterAware()) {
@@ -599,13 +755,18 @@ public class LettuceConnectionFactory
initialUris.add(createRedisURIAndApplySettings(node.getHost(), node.getPort()));
}
RedisClusterClient clusterClient = clientResources != null
? RedisClusterClient.create(clientResources, initialUris) : RedisClusterClient.create(initialUris);
RedisClusterClient clusterClient = clientConfiguration.getClientResources() //
.map(clientResources -> RedisClusterClient.create(clientResources, initialUris)) //
.orElseGet(() -> RedisClusterClient.create(initialUris));
this.clusterCommandExecutor = new ClusterCommandExecutor(
new LettuceClusterConnection.LettuceClusterTopologyProvider(clusterClient),
new LettuceClusterConnection.LettuceClusterNodeResourceProvider(clusterClient), EXCEPTION_TRANSLATION);
clientConfiguration.getClientOptions() //
.filter(clientOptions -> clientOptions instanceof ClusterClientOptions) //
.ifPresent(clientOptions -> clusterClient.setOptions((ClusterClientOptions) clientOptions));
return clusterClient;
}
@@ -613,16 +774,20 @@ public class LettuceConnectionFactory
return pool.getClient();
}
RedisURI uri = createRedisURIAndApplySettings(hostName, port);
return clientResources != null ? RedisClient.create(clientResources, uri) : RedisClient.create(uri);
RedisURI uri = createRedisURIAndApplySettings(getHostName(), getPort());
RedisClient redisClient = clientConfiguration.getClientResources() //
.map(clientResources -> RedisClient.create(clientResources, uri)) //
.orElseGet(() -> RedisClient.create(uri));
clientConfiguration.getClientOptions().ifPresent(redisClient::setOptions);
return redisClient;
}
private RedisURI getSentinelRedisURI() {
RedisURI redisUri = LettuceConverters.sentinelConfigurationToRedisURI(sentinelConfiguration);
if (StringUtils.hasText(password)) {
redisUri.setPassword(password);
if (StringUtils.hasText(getPassword())) {
redisUri.setPassword(getPassword());
}
return redisUri;
@@ -631,14 +796,14 @@ public class LettuceConnectionFactory
private RedisURI createRedisURIAndApplySettings(String host, int port) {
RedisURI.Builder builder = RedisURI.Builder.redis(host, port);
if (StringUtils.hasText(password)) {
builder.withPassword(password);
if (StringUtils.hasText(getPassword())) {
builder.withPassword(getPassword());
}
builder.withSsl(useSsl);
builder.withVerifyPeer(verifyPeer);
builder.withStartTls(startTls);
builder.withTimeout(timeout, TimeUnit.MILLISECONDS);
builder.withSsl(clientConfiguration.useSsl());
builder.withVerifyPeer(clientConfiguration.isVerifyPeer());
builder.withStartTls(clientConfiguration.isStartTls());
builder.withTimeout(clientConfiguration.getTimeout().toMillis(), TimeUnit.MILLISECONDS);
return builder.build();
}
@@ -651,4 +816,107 @@ public class LettuceConnectionFactory
}
return new LettuceSentinelConnection(((RedisClient) client).connectSentinel());
}
private MutableLettuceClientConfiguration getMutableConfiguration() {
Assert.state(clientConfiguration instanceof MutableLettuceClientConfiguration,
() -> String.format("Client configuration must be instance of MutableLettuceClientConfiguration but is %s",
ClassUtils.getShortName(clientConfiguration.getClass())));
return (MutableLettuceClientConfiguration) clientConfiguration;
}
private long getClientTimeout() {
return clientConfiguration.getTimeout().toMillis();
}
static class MutableLettuceClientConfiguration implements LettuceClientConfiguration {
private boolean useSsl;
private boolean verifyPeer = true;
private boolean startTls;
private ClientResources clientResources;
private Duration timeout = Duration.ofSeconds(RedisURI.DEFAULT_TIMEOUT);
private Duration shutdownTimeout = Duration.ofSeconds(RedisURI.DEFAULT_TIMEOUT);
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#useSsl()
*/
@Override
public boolean useSsl() {
return useSsl;
}
public void setUseSsl(boolean useSsl) {
this.useSsl = useSsl;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#isVerifyPeer()
*/
@Override
public boolean isVerifyPeer() {
return verifyPeer;
}
public void setVerifyPeer(boolean verifyPeer) {
this.verifyPeer = verifyPeer;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#isStartTls()
*/
@Override
public boolean isStartTls() {
return startTls;
}
public void setStartTls(boolean startTls) {
this.startTls = startTls;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#getClientResources()
*/
@Override
public Optional<ClientResources> getClientResources() {
return Optional.ofNullable(clientResources);
}
public void setClientResources(ClientResources clientResources) {
this.clientResources = clientResources;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#getClientOptions()
*/
@Override
public Optional<ClientOptions> getClientOptions() {
return Optional.empty();
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#getTimeout()
*/
@Override
public Duration getTimeout() {
return timeout;
}
public void setTimeout(Duration timeout) {
this.timeout = timeout;
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration#getShutdownTimeout()
*/
@Override
public Duration getShutdownTimeout() {
return shutdownTimeout;
}
public void setShutdownTimeout(Duration shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
}
}
}