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>
pull/34759/head
Andy Wilkinson 2 years ago
parent 69f31cb6c0
commit ac55caa463

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 Mark Paluch
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class }) @ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
@ -52,8 +55,10 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
JedisConnectionConfiguration(RedisProperties properties, JedisConnectionConfiguration(RedisProperties properties,
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider, ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration, ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
ObjectProvider<RedisClusterConfiguration> clusterConfiguration) { ObjectProvider<RedisClusterConfiguration> clusterConfiguration,
super(properties, standaloneConfigurationProvider, sentinelConfiguration, clusterConfiguration); ObjectProvider<RedisConnectionDetails> connectionDetailsProvider) {
super(properties, standaloneConfigurationProvider, sentinelConfiguration, clusterConfiguration,
connectionDetailsProvider);
} }
@Bean @Bean
@ -90,7 +95,9 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
private JedisClientConfigurationBuilder applyProperties(JedisClientConfigurationBuilder builder) { private JedisClientConfigurationBuilder applyProperties(JedisClientConfigurationBuilder builder) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); 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().getTimeout()).to(builder::readTimeout);
map.from(getProperties().getConnectTimeout()).to(builder::connectTimeout); map.from(getProperties().getConnectTimeout()).to(builder::connectTimeout);
map.from(getProperties().getClientName()).whenHasText().to(builder::clientName); map.from(getProperties().getClientName()).whenHasText().to(builder::clientName);
@ -117,8 +124,7 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
} }
private void customizeConfigurationFromUrl(JedisClientConfiguration.JedisClientConfigurationBuilder builder) { private void customizeConfigurationFromUrl(JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
ConnectionInfo connectionInfo = parseUrl(getProperties().getUrl()); if (urlUsesSsl()) {
if (connectionInfo.isUseSsl()) {
builder.useSsl(); builder.useSsl();
} }
} }

@ -52,6 +52,8 @@ import org.springframework.util.StringUtils;
* *
* @author Mark Paluch * @author Mark Paluch
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Moritz Halbritter
* @author Phillip Webb
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisClient.class) @ConditionalOnClass(RedisClient.class)
@ -61,8 +63,10 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
LettuceConnectionConfiguration(RedisProperties properties, LettuceConnectionConfiguration(RedisProperties properties,
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider, ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider, ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) { ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider); ObjectProvider<RedisConnectionDetails> connectionDetailsProvider) {
super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider,
connectionDetailsProvider);
} }
@Bean(destroyMethod = "shutdown") @Bean(destroyMethod = "shutdown")
@ -116,7 +120,7 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
private LettuceClientConfigurationBuilder applyProperties( private LettuceClientConfigurationBuilder applyProperties(
LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) { LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
if (getProperties().isSsl()) { if (getConnectionDetails() instanceof PropertiesRedisConnectionDetails && getProperties().isSsl()) {
builder.useSsl(); builder.useSsl();
} }
if (getProperties().getTimeout() != null) { if (getProperties().getTimeout() != null) {
@ -161,8 +165,7 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
} }
private void customizeConfigurationFromUrl(LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) { private void customizeConfigurationFromUrl(LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
ConnectionInfo connectionInfo = parseUrl(getProperties().getUrl()); if (urlUsesSsl()) {
if (connectionInfo.isUseSsl()) {
builder.useSsl(); 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 java.util.List;
import org.springframework.beans.factory.ObjectProvider; 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.boot.autoconfigure.data.redis.RedisProperties.Pool;
import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode; 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.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/** /**
* Base Redis connection configuration. * Base Redis connection configuration.
@ -39,6 +41,9 @@ import org.springframework.util.StringUtils;
* @author Alen Turkovic * @author Alen Turkovic
* @author Scott Frederick * @author Scott Frederick
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
abstract class RedisConnectionConfiguration { abstract class RedisConnectionConfiguration {
@ -53,14 +58,19 @@ abstract class RedisConnectionConfiguration {
private final RedisClusterConfiguration clusterConfiguration; private final RedisClusterConfiguration clusterConfiguration;
private final RedisConnectionDetails connectionDetails;
protected RedisConnectionConfiguration(RedisProperties properties, protected RedisConnectionConfiguration(RedisProperties properties,
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider, ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider, ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) { ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
ObjectProvider<RedisConnectionDetails> connectionDetailsProvider) {
this.properties = properties; this.properties = properties;
this.standaloneConfiguration = standaloneConfigurationProvider.getIfAvailable(); this.standaloneConfiguration = standaloneConfigurationProvider.getIfAvailable();
this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable(); this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable(); this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
this.connectionDetails = connectionDetailsProvider
.getIfAvailable(() -> new PropertiesRedisConnectionDetails(properties));
} }
protected final RedisStandaloneConfiguration getStandaloneConfig() { protected final RedisStandaloneConfiguration getStandaloneConfig() {
@ -68,20 +78,11 @@ abstract class RedisConnectionConfiguration {
return this.standaloneConfiguration; return this.standaloneConfiguration;
} }
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
if (StringUtils.hasText(this.properties.getUrl())) { config.setHostName(this.connectionDetails.getStandalone().getHost());
ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl()); config.setPort(this.connectionDetails.getStandalone().getPort());
config.setHostName(connectionInfo.getHostName()); config.setUsername(this.connectionDetails.getUsername());
config.setPort(connectionInfo.getPort()); config.setPassword(RedisPassword.of(this.connectionDetails.getPassword()));
config.setUsername(connectionInfo.getUsername()); config.setDatabase(this.connectionDetails.getStandalone().getDatabase());
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());
return config; return config;
} }
@ -89,20 +90,21 @@ abstract class RedisConnectionConfiguration {
if (this.sentinelConfiguration != null) { if (this.sentinelConfiguration != null) {
return this.sentinelConfiguration; return this.sentinelConfiguration;
} }
RedisProperties.Sentinel sentinelProperties = this.properties.getSentinel(); if (this.connectionDetails.getSentinel() != null) {
if (sentinelProperties != null) {
RedisSentinelConfiguration config = new RedisSentinelConfiguration(); RedisSentinelConfiguration config = new RedisSentinelConfiguration();
config.master(sentinelProperties.getMaster()); config.master(this.connectionDetails.getSentinel().getMaster());
config.setSentinels(createSentinels(sentinelProperties)); config.setSentinels(createSentinels(this.connectionDetails.getSentinel()));
config.setUsername(this.properties.getUsername()); config.setUsername(this.connectionDetails.getUsername());
if (this.properties.getPassword() != null) { String password = this.connectionDetails.getPassword();
config.setPassword(RedisPassword.of(this.properties.getPassword())); if (password != null) {
} config.setPassword(RedisPassword.of(password));
config.setSentinelUsername(sentinelProperties.getUsername()); }
if (sentinelProperties.getPassword() != null) { config.setSentinelUsername(this.connectionDetails.getSentinel().getUsername());
config.setSentinelPassword(RedisPassword.of(sentinelProperties.getPassword())); String sentinelPassword = this.connectionDetails.getSentinel().getPassword();
} if (sentinelPassword != null) {
config.setDatabase(this.properties.getDatabase()); config.setSentinelPassword(RedisPassword.of(sentinelPassword));
}
config.setDatabase(this.connectionDetails.getSentinel().getDatabase());
return config; return config;
} }
return null; return null;
@ -116,20 +118,26 @@ abstract class RedisConnectionConfiguration {
if (this.clusterConfiguration != null) { if (this.clusterConfiguration != null) {
return this.clusterConfiguration; return this.clusterConfiguration;
} }
if (this.properties.getCluster() == null) {
return null;
}
RedisProperties.Cluster clusterProperties = this.properties.getCluster(); RedisProperties.Cluster clusterProperties = this.properties.getCluster();
RedisClusterConfiguration config = new RedisClusterConfiguration(clusterProperties.getNodes()); if (this.connectionDetails.getCluster() != null) {
if (clusterProperties.getMaxRedirects() != null) { RedisClusterConfiguration config = new RedisClusterConfiguration(
getNodes(this.connectionDetails.getCluster()));
if (clusterProperties != null && clusterProperties.getMaxRedirects() != null) {
config.setMaxRedirects(clusterProperties.getMaxRedirects()); config.setMaxRedirects(clusterProperties.getMaxRedirects());
} }
config.setUsername(this.properties.getUsername()); config.setUsername(this.connectionDetails.getUsername());
if (this.properties.getPassword() != null) { String password = this.connectionDetails.getPassword();
config.setPassword(RedisPassword.of(this.properties.getPassword())); if (password != null) {
config.setPassword(RedisPassword.of(password));
} }
return config; 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() { protected final RedisProperties getProperties() {
return this.properties; return this.properties;
@ -140,20 +148,23 @@ abstract class RedisConnectionConfiguration {
return (enabled != null) ? enabled : COMMONS_POOL2_AVAILABLE; 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<>(); List<RedisNode> nodes = new ArrayList<>();
for (String node : sentinel.getNodes()) { for (Node node : sentinel.getNodes()) {
try { nodes.add(new RedisNode(node.host(), node.port()));
nodes.add(RedisNode.fromString(node));
} }
catch (RuntimeException ex) { return nodes;
throw new IllegalStateException("Invalid redis sentinel property '" + node + "'", ex);
} }
protected final boolean urlUsesSsl() {
return parseUrl(this.properties.getUrl()).isUseSsl();
} }
return nodes;
protected final RedisConnectionDetails getConnectionDetails() {
return this.connectionDetails;
} }
protected ConnectionInfo parseUrl(String url) { static ConnectionInfo parseUrl(String url) {
try { try {
URI uri = new URI(url); URI uri = new URI(url);
String scheme = uri.getScheme(); String scheme = uri.getScheme();
@ -198,16 +209,12 @@ abstract class RedisConnectionConfiguration {
this.password = password; this.password = password;
} }
boolean isUseSsl() { URI getUri() {
return this.useSsl; return this.uri;
}
String getHostName() {
return this.uri.getHost();
} }
int getPort() { boolean isUseSsl() {
return this.uri.getPort(); return this.useSsl;
} }
String getUsername() { String getUsername() {

@ -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 Mark Paluch
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Weix Sun * @author Weix Sun
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@ClassPathExclusions("lettuce-core-*.jar") @ClassPathExclusions("lettuce-core-*.jar")
class RedisAutoConfigurationJedisTests { 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 @Test
void testRedisUrlConfiguration() { void testRedisUrlConfiguration() {
this.contextRunner 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) @Configuration(proxyBeanMethods = false)
static class JedisConnectionFactoryCaptorConfiguration { 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.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode; 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.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
@ -71,6 +72,9 @@ import static org.mockito.Mockito.mock;
* @author Alen Turkovic * @author Alen Turkovic
* @author Scott Frederick * @author Scott Frederick
* @author Weix Sun * @author Weix Sun
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
class RedisAutoConfigurationTests { class RedisAutoConfigurationTests {
@ -490,6 +494,51 @@ class RedisAutoConfigurationTests {
(options) -> assertThat(options.getTopologyRefreshOptions().useDynamicRefreshSources()).isTrue())); (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( private <T extends ClientOptions> ContextConsumer<AssertableApplicationContext> assertClientOptions(
Class<T> expectedType, Consumer<T> options) { Class<T> expectedType, Consumer<T> options) {
return (context) -> { 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));
}
};
}
};
}
}
} }

Loading…
Cancel
Save