Add ConnectionDetail support to Redis auto-configuration
Update Redis auto-configuration so that `RedisConnectionDetails` beans may be optionally used to provide connection details. See gh-34657 Co-Authored-By: Mortitz Halbritter <mkammerer@vmware.com> Co-Authored-By: Phillip Webb <pwebb@vmware.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* Copyright 2012-2023 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.
|
||||
@@ -42,6 +42,9 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
|
||||
@@ -52,8 +55,10 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
JedisConnectionConfiguration(RedisProperties properties,
|
||||
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
|
||||
ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfiguration) {
|
||||
super(properties, standaloneConfigurationProvider, sentinelConfiguration, clusterConfiguration);
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfiguration,
|
||||
ObjectProvider<RedisConnectionDetails> connectionDetailsProvider) {
|
||||
super(properties, standaloneConfigurationProvider, sentinelConfiguration, clusterConfiguration,
|
||||
connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -90,7 +95,9 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
|
||||
private JedisClientConfigurationBuilder applyProperties(JedisClientConfigurationBuilder builder) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(getProperties().isSsl()).whenTrue().toCall(builder::useSsl);
|
||||
boolean ssl = (!(getConnectionDetails() instanceof PropertiesRedisConnectionDetails)) ? false
|
||||
: getProperties().isSsl();
|
||||
map.from(ssl).whenTrue().toCall(builder::useSsl);
|
||||
map.from(getProperties().getTimeout()).to(builder::readTimeout);
|
||||
map.from(getProperties().getConnectTimeout()).to(builder::connectTimeout);
|
||||
map.from(getProperties().getClientName()).whenHasText().to(builder::clientName);
|
||||
@@ -117,8 +124,7 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
}
|
||||
|
||||
private void customizeConfigurationFromUrl(JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
|
||||
ConnectionInfo connectionInfo = parseUrl(getProperties().getUrl());
|
||||
if (connectionInfo.isUseSsl()) {
|
||||
if (urlUsesSsl()) {
|
||||
builder.useSsl();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(RedisClient.class)
|
||||
@@ -61,8 +63,10 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
LettuceConnectionConfiguration(RedisProperties properties,
|
||||
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
|
||||
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
|
||||
super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider);
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
|
||||
ObjectProvider<RedisConnectionDetails> connectionDetailsProvider) {
|
||||
super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider,
|
||||
connectionDetailsProvider);
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "shutdown")
|
||||
@@ -116,7 +120,7 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
|
||||
private LettuceClientConfigurationBuilder applyProperties(
|
||||
LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
|
||||
if (getProperties().isSsl()) {
|
||||
if (getConnectionDetails() instanceof PropertiesRedisConnectionDetails && getProperties().isSsl()) {
|
||||
builder.useSsl();
|
||||
}
|
||||
if (getProperties().getTimeout() != null) {
|
||||
@@ -161,8 +165,7 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
|
||||
}
|
||||
|
||||
private void customizeConfigurationFromUrl(LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
|
||||
ConnectionInfo connectionInfo = parseUrl(getProperties().getUrl());
|
||||
if (connectionInfo.isUseSsl()) {
|
||||
if (urlUsesSsl()) {
|
||||
builder.useSsl();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.data.redis;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionConfiguration.ConnectionInfo;
|
||||
|
||||
/**
|
||||
* Adapts {@link RedisProperties} to {@link RedisConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class PropertiesRedisConnectionDetails implements RedisConnectionDetails {
|
||||
|
||||
private final RedisProperties properties;
|
||||
|
||||
PropertiesRedisConnectionDetails(RedisProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
if (this.properties.getUrl() != null) {
|
||||
ConnectionInfo connectionInfo = connectionInfo(this.properties.getUrl());
|
||||
String userInfo = connectionInfo.getUri().getUserInfo();
|
||||
int index = (userInfo != null) ? userInfo.indexOf(':') : -1;
|
||||
if (index != -1) {
|
||||
return userInfo.substring(0, index);
|
||||
}
|
||||
}
|
||||
return this.properties.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
if (this.properties.getUrl() != null) {
|
||||
ConnectionInfo connectionInfo = connectionInfo(this.properties.getUrl());
|
||||
String userInfo = connectionInfo.getUri().getUserInfo();
|
||||
int index = (userInfo != null) ? userInfo.indexOf(':') : -1;
|
||||
if (index != -1) {
|
||||
return userInfo.substring(index + 1);
|
||||
}
|
||||
}
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Standalone getStandalone() {
|
||||
if (this.properties.getUrl() != null) {
|
||||
ConnectionInfo connectionInfo = connectionInfo(this.properties.getUrl());
|
||||
return Standalone.of(connectionInfo.getUri().getHost(), connectionInfo.getUri().getPort(),
|
||||
this.properties.getDatabase());
|
||||
}
|
||||
return Standalone.of(this.properties.getHost(), this.properties.getPort(), this.properties.getDatabase());
|
||||
}
|
||||
|
||||
private ConnectionInfo connectionInfo(String url) {
|
||||
return (url != null) ? RedisConnectionConfiguration.parseUrl(url) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sentinel getSentinel() {
|
||||
org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel sentinel = this.properties
|
||||
.getSentinel();
|
||||
if (sentinel == null) {
|
||||
return null;
|
||||
}
|
||||
return new Sentinel() {
|
||||
|
||||
@Override
|
||||
public int getDatabase() {
|
||||
return PropertiesRedisConnectionDetails.this.properties.getDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMaster() {
|
||||
return sentinel.getMaster();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes() {
|
||||
return sentinel.getNodes().stream().map(PropertiesRedisConnectionDetails.this::asNode).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return sentinel.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return sentinel.getPassword();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cluster getCluster() {
|
||||
RedisProperties.Cluster cluster = this.properties.getCluster();
|
||||
List<Node> nodes = (cluster != null) ? cluster.getNodes().stream().map(this::asNode).toList() : null;
|
||||
return (nodes != null) ? () -> nodes : null;
|
||||
}
|
||||
|
||||
private Node asNode(String node) {
|
||||
String[] components = node.split(":");
|
||||
return new Node(components[0], Integer.parseInt(components[1]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* Copyright 2012-2023 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.
|
||||
@@ -22,6 +22,9 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails.Cluster;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails.Node;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails.Sentinel;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
@@ -29,7 +32,6 @@ import org.springframework.data.redis.connection.RedisPassword;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base Redis connection configuration.
|
||||
@@ -39,6 +41,9 @@ import org.springframework.util.StringUtils;
|
||||
* @author Alen Turkovic
|
||||
* @author Scott Frederick
|
||||
* @author Eddú Meléndez
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class RedisConnectionConfiguration {
|
||||
|
||||
@@ -53,14 +58,19 @@ abstract class RedisConnectionConfiguration {
|
||||
|
||||
private final RedisClusterConfiguration clusterConfiguration;
|
||||
|
||||
private final RedisConnectionDetails connectionDetails;
|
||||
|
||||
protected RedisConnectionConfiguration(RedisProperties properties,
|
||||
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
|
||||
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
|
||||
ObjectProvider<RedisConnectionDetails> connectionDetailsProvider) {
|
||||
this.properties = properties;
|
||||
this.standaloneConfiguration = standaloneConfigurationProvider.getIfAvailable();
|
||||
this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
|
||||
this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
|
||||
this.connectionDetails = connectionDetailsProvider
|
||||
.getIfAvailable(() -> new PropertiesRedisConnectionDetails(properties));
|
||||
}
|
||||
|
||||
protected final RedisStandaloneConfiguration getStandaloneConfig() {
|
||||
@@ -68,20 +78,11 @@ abstract class RedisConnectionConfiguration {
|
||||
return this.standaloneConfiguration;
|
||||
}
|
||||
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
|
||||
if (StringUtils.hasText(this.properties.getUrl())) {
|
||||
ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
|
||||
config.setHostName(connectionInfo.getHostName());
|
||||
config.setPort(connectionInfo.getPort());
|
||||
config.setUsername(connectionInfo.getUsername());
|
||||
config.setPassword(RedisPassword.of(connectionInfo.getPassword()));
|
||||
}
|
||||
else {
|
||||
config.setHostName(this.properties.getHost());
|
||||
config.setPort(this.properties.getPort());
|
||||
config.setUsername(this.properties.getUsername());
|
||||
config.setPassword(RedisPassword.of(this.properties.getPassword()));
|
||||
}
|
||||
config.setDatabase(this.properties.getDatabase());
|
||||
config.setHostName(this.connectionDetails.getStandalone().getHost());
|
||||
config.setPort(this.connectionDetails.getStandalone().getPort());
|
||||
config.setUsername(this.connectionDetails.getUsername());
|
||||
config.setPassword(RedisPassword.of(this.connectionDetails.getPassword()));
|
||||
config.setDatabase(this.connectionDetails.getStandalone().getDatabase());
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -89,20 +90,21 @@ abstract class RedisConnectionConfiguration {
|
||||
if (this.sentinelConfiguration != null) {
|
||||
return this.sentinelConfiguration;
|
||||
}
|
||||
RedisProperties.Sentinel sentinelProperties = this.properties.getSentinel();
|
||||
if (sentinelProperties != null) {
|
||||
if (this.connectionDetails.getSentinel() != null) {
|
||||
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
|
||||
config.master(sentinelProperties.getMaster());
|
||||
config.setSentinels(createSentinels(sentinelProperties));
|
||||
config.setUsername(this.properties.getUsername());
|
||||
if (this.properties.getPassword() != null) {
|
||||
config.setPassword(RedisPassword.of(this.properties.getPassword()));
|
||||
config.master(this.connectionDetails.getSentinel().getMaster());
|
||||
config.setSentinels(createSentinels(this.connectionDetails.getSentinel()));
|
||||
config.setUsername(this.connectionDetails.getUsername());
|
||||
String password = this.connectionDetails.getPassword();
|
||||
if (password != null) {
|
||||
config.setPassword(RedisPassword.of(password));
|
||||
}
|
||||
config.setSentinelUsername(sentinelProperties.getUsername());
|
||||
if (sentinelProperties.getPassword() != null) {
|
||||
config.setSentinelPassword(RedisPassword.of(sentinelProperties.getPassword()));
|
||||
config.setSentinelUsername(this.connectionDetails.getSentinel().getUsername());
|
||||
String sentinelPassword = this.connectionDetails.getSentinel().getPassword();
|
||||
if (sentinelPassword != null) {
|
||||
config.setSentinelPassword(RedisPassword.of(sentinelPassword));
|
||||
}
|
||||
config.setDatabase(this.properties.getDatabase());
|
||||
config.setDatabase(this.connectionDetails.getSentinel().getDatabase());
|
||||
return config;
|
||||
}
|
||||
return null;
|
||||
@@ -116,19 +118,25 @@ abstract class RedisConnectionConfiguration {
|
||||
if (this.clusterConfiguration != null) {
|
||||
return this.clusterConfiguration;
|
||||
}
|
||||
if (this.properties.getCluster() == null) {
|
||||
return null;
|
||||
}
|
||||
RedisProperties.Cluster clusterProperties = this.properties.getCluster();
|
||||
RedisClusterConfiguration config = new RedisClusterConfiguration(clusterProperties.getNodes());
|
||||
if (clusterProperties.getMaxRedirects() != null) {
|
||||
config.setMaxRedirects(clusterProperties.getMaxRedirects());
|
||||
if (this.connectionDetails.getCluster() != null) {
|
||||
RedisClusterConfiguration config = new RedisClusterConfiguration(
|
||||
getNodes(this.connectionDetails.getCluster()));
|
||||
if (clusterProperties != null && clusterProperties.getMaxRedirects() != null) {
|
||||
config.setMaxRedirects(clusterProperties.getMaxRedirects());
|
||||
}
|
||||
config.setUsername(this.connectionDetails.getUsername());
|
||||
String password = this.connectionDetails.getPassword();
|
||||
if (password != null) {
|
||||
config.setPassword(RedisPassword.of(password));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
config.setUsername(this.properties.getUsername());
|
||||
if (this.properties.getPassword() != null) {
|
||||
config.setPassword(RedisPassword.of(this.properties.getPassword()));
|
||||
}
|
||||
return config;
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<String> getNodes(Cluster cluster) {
|
||||
return cluster.getNodes().stream().map((node) -> "%s:%d".formatted(node.host(), node.port())).toList();
|
||||
}
|
||||
|
||||
protected final RedisProperties getProperties() {
|
||||
@@ -140,20 +148,23 @@ abstract class RedisConnectionConfiguration {
|
||||
return (enabled != null) ? enabled : COMMONS_POOL2_AVAILABLE;
|
||||
}
|
||||
|
||||
private List<RedisNode> createSentinels(RedisProperties.Sentinel sentinel) {
|
||||
private List<RedisNode> createSentinels(Sentinel sentinel) {
|
||||
List<RedisNode> nodes = new ArrayList<>();
|
||||
for (String node : sentinel.getNodes()) {
|
||||
try {
|
||||
nodes.add(RedisNode.fromString(node));
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw new IllegalStateException("Invalid redis sentinel property '" + node + "'", ex);
|
||||
}
|
||||
for (Node node : sentinel.getNodes()) {
|
||||
nodes.add(new RedisNode(node.host(), node.port()));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
protected ConnectionInfo parseUrl(String url) {
|
||||
protected final boolean urlUsesSsl() {
|
||||
return parseUrl(this.properties.getUrl()).isUseSsl();
|
||||
}
|
||||
|
||||
protected final RedisConnectionDetails getConnectionDetails() {
|
||||
return this.connectionDetails;
|
||||
}
|
||||
|
||||
static ConnectionInfo parseUrl(String url) {
|
||||
try {
|
||||
URI uri = new URI(url);
|
||||
String scheme = uri.getScheme();
|
||||
@@ -198,18 +209,14 @@ abstract class RedisConnectionConfiguration {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
URI getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
boolean isUseSsl() {
|
||||
return this.useSsl;
|
||||
}
|
||||
|
||||
String getHostName() {
|
||||
return this.uri.getHost();
|
||||
}
|
||||
|
||||
int getPort() {
|
||||
return this.uri.getPort();
|
||||
}
|
||||
|
||||
String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.data.redis;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a Redis service.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface RedisConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Login username of the redis server.
|
||||
* @return the login username of the redis server
|
||||
*/
|
||||
default String getUsername() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login password of the redis server.
|
||||
* @return the login password of the redis server
|
||||
*/
|
||||
default String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis standalone configuration. Mutually exclusive with {@link #getSentinel()} and
|
||||
* {@link #getCluster()}.
|
||||
* @return the Redis standalone configuration
|
||||
*/
|
||||
default Standalone getStandalone() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis sentinel configuration. Mutually exclusive with {@link #getStandalone()} and
|
||||
* {@link #getCluster()}.
|
||||
* @return the Redis sentinel configuration
|
||||
*/
|
||||
default Sentinel getSentinel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis cluster configuration. Mutually exclusive with {@link #getStandalone()} and
|
||||
* {@link #getSentinel()}.
|
||||
* @return the Redis cluster configuration
|
||||
*/
|
||||
default Cluster getCluster() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis standalone configuration.
|
||||
*/
|
||||
interface Standalone {
|
||||
|
||||
/**
|
||||
* Redis server host.
|
||||
* @return the redis server host
|
||||
*/
|
||||
String getHost();
|
||||
|
||||
/**
|
||||
* Redis server port.
|
||||
* @return the redis server port
|
||||
*/
|
||||
int getPort();
|
||||
|
||||
/**
|
||||
* Database index used by the connection factory.
|
||||
* @return the database index used by the connection factory
|
||||
*/
|
||||
default int getDatabase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Standalone of(String host, int port) {
|
||||
return of(host, port, 0);
|
||||
}
|
||||
|
||||
static Standalone of(String host, int port, int database) {
|
||||
Assert.hasLength(host, "Host must not be empty");
|
||||
return new Standalone() {
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis sentinel configuration.
|
||||
*/
|
||||
interface Sentinel {
|
||||
|
||||
/**
|
||||
* Database index used by the connection factory.
|
||||
* @return the database index used by the connection factory
|
||||
*/
|
||||
int getDatabase();
|
||||
|
||||
/**
|
||||
* Name of the Redis server.
|
||||
* @return the name of the Redis server
|
||||
*/
|
||||
String getMaster();
|
||||
|
||||
/**
|
||||
* List of nodes.
|
||||
* @return the list of nodes
|
||||
*/
|
||||
List<Node> getNodes();
|
||||
|
||||
/**
|
||||
* Login username for authenticating with sentinel(s).
|
||||
* @return the login username for authenticating with sentinel(s) or {@code null}
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* Password for authenticating with sentinel(s).
|
||||
* @return the password for authenticating with sentinel(s) or {@code null}
|
||||
*/
|
||||
String getPassword();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis cluster configuration.
|
||||
*/
|
||||
interface Cluster {
|
||||
|
||||
/**
|
||||
* Nodes to bootstrap from. This represents an "initial" list of cluster nodes and
|
||||
* is required to have at least one entry.
|
||||
* @return nodes to bootstrap from
|
||||
*/
|
||||
List<Node> getNodes();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A node in a sentinel or cluster configuration.
|
||||
*
|
||||
* @param host the hostname of the node
|
||||
* @param port the port of the node
|
||||
*/
|
||||
record Node(String host, int port) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,6 +39,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Mark Paluch
|
||||
* @author Stephane Nicoll
|
||||
* @author Weix Sun
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@ClassPathExclusions("lettuce-core-*.jar")
|
||||
class RedisAutoConfigurationJedisTests {
|
||||
@@ -79,6 +82,14 @@ class RedisAutoConfigurationJedisTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class).run((context) -> {
|
||||
JedisConnectionFactory cf = context.getBean(JedisConnectionFactory.class);
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisUrlConfiguration() {
|
||||
this.contextRunner
|
||||
@@ -240,6 +251,35 @@ class RedisAutoConfigurationJedisTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
RedisConnectionDetails redisConnectionDetails() {
|
||||
return new RedisConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public Standalone getStandalone() {
|
||||
return new Standalone() {
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return "localhost";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return 6379;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class JedisConnectionFactoryCaptorConfiguration {
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
import org.springframework.data.redis.connection.RedisPassword;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
@@ -71,6 +72,9 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Alen Turkovic
|
||||
* @author Scott Frederick
|
||||
* @author Weix Sun
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class RedisAutoConfigurationTests {
|
||||
|
||||
@@ -490,6 +494,51 @@ class RedisAutoConfigurationTests {
|
||||
(options) -> assertThat(options.getTopologyRefreshOptions().useDynamicRefreshSources()).isTrue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesStandaloneFromConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsStandaloneConfiguration.class).run((context) -> {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
RedisStandaloneConfiguration configuration = cf.getStandaloneConfiguration();
|
||||
assertThat(configuration.getHostName()).isEqualTo("redis.example.com");
|
||||
assertThat(configuration.getPort()).isEqualTo(16379);
|
||||
assertThat(configuration.getDatabase()).isOne();
|
||||
assertThat(configuration.getUsername()).isEqualTo("user-1");
|
||||
assertThat(configuration.getPassword()).isEqualTo(RedisPassword.of("password-1"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesSentinelFromConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsSentinelConfiguration.class).run((context) -> {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
RedisSentinelConfiguration configuration = cf.getSentinelConfiguration();
|
||||
assertThat(configuration).isNotNull();
|
||||
assertThat(configuration.getSentinelUsername()).isEqualTo("sentinel-1");
|
||||
assertThat(configuration.getSentinelPassword().get()).isEqualTo("secret-1".toCharArray());
|
||||
assertThat(configuration.getSentinels()).containsExactly(new RedisNode("node-1", 12345));
|
||||
assertThat(configuration.getUsername()).isEqualTo("user-1");
|
||||
assertThat(configuration.getPassword()).isEqualTo(RedisPassword.of("password-1"));
|
||||
assertThat(configuration.getDatabase()).isOne();
|
||||
assertThat(configuration.getMaster().getName()).isEqualTo("master.redis.example.com");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesClusterFromConnectionDetailsIfAvailable() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsClusterConfiguration.class).run((context) -> {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
RedisClusterConfiguration configuration = cf.getClusterConfiguration();
|
||||
assertThat(configuration).isNotNull();
|
||||
assertThat(configuration.getUsername()).isEqualTo("user-1");
|
||||
assertThat(configuration.getPassword().get()).isEqualTo("password-1".toCharArray());
|
||||
assertThat(configuration.getClusterNodes()).containsExactly(new RedisNode("node-1", 12345),
|
||||
new RedisNode("node-2", 23456));
|
||||
});
|
||||
}
|
||||
|
||||
private <T extends ClientOptions> ContextConsumer<AssertableApplicationContext> assertClientOptions(
|
||||
Class<T> expectedType, Consumer<T> options) {
|
||||
return (context) -> {
|
||||
@@ -532,4 +581,136 @@ class RedisAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsStandaloneConfiguration {
|
||||
|
||||
@Bean
|
||||
RedisConnectionDetails redisConnectionDetails() {
|
||||
return new RedisConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Standalone getStandalone() {
|
||||
return new Standalone() {
|
||||
|
||||
@Override
|
||||
public int getDatabase() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return "redis.example.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return 16379;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsSentinelConfiguration {
|
||||
|
||||
@Bean
|
||||
RedisConnectionDetails redisConnectionDetails() {
|
||||
return new RedisConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sentinel getSentinel() {
|
||||
return new Sentinel() {
|
||||
|
||||
@Override
|
||||
public int getDatabase() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMaster() {
|
||||
return "master.redis.example.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes() {
|
||||
return List.of(new Node("node-1", 12345));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "sentinel-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "secret-1";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsClusterConfiguration {
|
||||
|
||||
@Bean
|
||||
RedisConnectionDetails redisConnectionDetails() {
|
||||
return new RedisConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cluster getCluster() {
|
||||
return new Cluster() {
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes() {
|
||||
return List.of(new Node("node-1", 12345), new Node("node-2", 23456));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user