diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java index 8299220e5..a1bb43630 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java @@ -31,6 +31,7 @@ import org.springframework.data.redis.connection.RedisConfiguration.ClusterConfi import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.NumberUtils; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -223,6 +224,44 @@ public class RedisClusterConfiguration implements RedisConfiguration, ClusterCon this.password = password; } + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof RedisClusterConfiguration)) { + return false; + } + RedisClusterConfiguration that = (RedisClusterConfiguration) o; + if (!ObjectUtils.nullSafeEquals(clusterNodes, that.clusterNodes)) { + return false; + } + if (!ObjectUtils.nullSafeEquals(maxRedirects, that.maxRedirects)) { + return false; + } + if (!ObjectUtils.nullSafeEquals(username, that.username)) { + return false; + } + return ObjectUtils.nullSafeEquals(password, that.password); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int result = ObjectUtils.nullSafeHashCode(clusterNodes); + result = 31 * result + ObjectUtils.nullSafeHashCode(maxRedirects); + result = 31 * result + ObjectUtils.nullSafeHashCode(username); + result = 31 * result + ObjectUtils.nullSafeHashCode(password); + return result; + } + private RedisNode readHostAndPortFromString(String hostAndPort) { String[] args = split(hostAndPort, ":"); diff --git a/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java index c334efb1c..f8cac2cb5 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java @@ -349,11 +349,11 @@ public interface RedisConfiguration { * * @param name must not be {@literal null}. */ - default void setMaster(final String name) { + default void setMaster(String name) { Assert.notNull(name, "Name of sentinel master must not be null."); - setMaster(() -> name); + setMaster(new SentinelMasterId(name)); } /** diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java index ab93a5534..2840225cb 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java @@ -28,6 +28,7 @@ import org.springframework.core.env.PropertySource; import org.springframework.data.redis.connection.RedisConfiguration.SentinelConfiguration; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -289,6 +290,52 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC return sentinelPassword; } + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof RedisSentinelConfiguration)) { + return false; + } + RedisSentinelConfiguration that = (RedisSentinelConfiguration) o; + if (database != that.database) { + return false; + } + if (!ObjectUtils.nullSafeEquals(master, that.master)) { + return false; + } + if (!ObjectUtils.nullSafeEquals(sentinels, that.sentinels)) { + return false; + } + if (!ObjectUtils.nullSafeEquals(dataNodeUsername, that.dataNodeUsername)) { + return false; + } + if (!ObjectUtils.nullSafeEquals(dataNodePassword, that.dataNodePassword)) { + return false; + } + return ObjectUtils.nullSafeEquals(sentinelPassword, that.sentinelPassword); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int result = ObjectUtils.nullSafeHashCode(master); + result = 31 * result + ObjectUtils.nullSafeHashCode(sentinels); + result = 31 * result + database; + result = 31 * result + ObjectUtils.nullSafeHashCode(dataNodeUsername); + result = 31 * result + ObjectUtils.nullSafeHashCode(dataNodePassword); + result = 31 * result + ObjectUtils.nullSafeHashCode(sentinelPassword); + return result; + } + private RedisNode readHostAndPortFromString(String hostAndPort) { String[] args = split(hostAndPort, ":"); diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java index 7727fb6aa..841f0964e 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java @@ -18,6 +18,7 @@ package org.springframework.data.redis.connection; import org.springframework.data.redis.connection.RedisConfiguration.DomainSocketConfiguration; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Configuration class used for setting up {@link RedisConnection} via {@link RedisConnectionFactory} connecting to @@ -133,4 +134,42 @@ public class RedisSocketConfiguration implements RedisConfiguration, DomainSocke this.password = password; } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof RedisSocketConfiguration)) { + return false; + } + RedisSocketConfiguration that = (RedisSocketConfiguration) o; + if (database != that.database) { + return false; + } + if (!ObjectUtils.nullSafeEquals(socket, that.socket)) { + return false; + } + if (!ObjectUtils.nullSafeEquals(username, that.username)) { + return false; + } + return ObjectUtils.nullSafeEquals(password, that.password); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int result = ObjectUtils.nullSafeHashCode(socket); + result = 31 * result + database; + result = 31 * result + ObjectUtils.nullSafeHashCode(username); + result = 31 * result + ObjectUtils.nullSafeHashCode(password); + return result; + } } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java index 424edec84..022e41b6d 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java @@ -20,6 +20,7 @@ import org.springframework.data.redis.connection.RedisConfiguration.WithHostAndP import org.springframework.data.redis.connection.RedisConfiguration.WithPassword; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Configuration class used for setting up {@link RedisConnection} via {@link RedisConnectionFactory} using connecting @@ -166,4 +167,46 @@ public class RedisStandaloneConfiguration this.password = password; } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof RedisStandaloneConfiguration)) { + return false; + } + RedisStandaloneConfiguration that = (RedisStandaloneConfiguration) o; + if (port != that.port) { + return false; + } + if (database != that.database) { + return false; + } + if (!ObjectUtils.nullSafeEquals(hostName, that.hostName)) { + return false; + } + if (!ObjectUtils.nullSafeEquals(username, that.username)) { + return false; + } + return ObjectUtils.nullSafeEquals(password, that.password); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int result = ObjectUtils.nullSafeHashCode(hostName); + result = 31 * result + port; + result = 31 * result + database; + result = 31 * result + ObjectUtils.nullSafeHashCode(username); + result = 31 * result + ObjectUtils.nullSafeHashCode(password); + return result; + } } diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java index de4a4e321..a2c9bde31 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java @@ -22,6 +22,7 @@ import java.util.List; import org.springframework.data.redis.connection.RedisConfiguration.StaticMasterReplicaConfiguration; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Configuration class used for setting up {@link RedisConnection} via {@link RedisConnectionFactory} using the provided @@ -181,4 +182,42 @@ public class RedisStaticMasterReplicaConfiguration implements RedisConfiguration public List getNodes() { return Collections.unmodifiableList(nodes); } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof RedisStaticMasterReplicaConfiguration)) { + return false; + } + RedisStaticMasterReplicaConfiguration that = (RedisStaticMasterReplicaConfiguration) o; + if (database != that.database) { + return false; + } + if (!ObjectUtils.nullSafeEquals(nodes, that.nodes)) { + return false; + } + if (!ObjectUtils.nullSafeEquals(username, that.username)) { + return false; + } + return ObjectUtils.nullSafeEquals(password, that.password); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int result = ObjectUtils.nullSafeHashCode(nodes); + result = 31 * result + database; + result = 31 * result + ObjectUtils.nullSafeHashCode(username); + result = 31 * result + ObjectUtils.nullSafeHashCode(password); + return result; + } } diff --git a/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java b/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java new file mode 100644 index 000000000..23c608556 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/connection/SentinelMasterId.java @@ -0,0 +1,80 @@ +/* + * Copyright 2021 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.data.redis.connection; + +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +/** + * Simple {@link NamedNode}. + * + * @author Mark Paluch + * @since 2.5.3 + */ +class SentinelMasterId implements NamedNode { + + private final String name; + + public SentinelMasterId(String name) { + Assert.hasText(name, "Sentinel Master Id must not be null or empty"); + this.name = name; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.NamedNode#getName() + */ + @NonNull + @Override + public String getName() { + return name; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return getName(); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SentinelMasterId)) { + return false; + } + SentinelMasterId that = (SentinelMasterId) o; + return ObjectUtils.nullSafeEquals(name, that.name); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return ObjectUtils.nullSafeHashCode(name); + } +} diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java index 40b9b849f..e62935208 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfiguration.java @@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Redis client configuration for lettuce. This configuration provides optional configuration elements such as @@ -173,6 +174,30 @@ public interface LettuceClientConfiguration { LettuceClientConfigurationBuilder() {} + /** + * Apply SSL settings, command timeout, and client name from a {@link RedisURI}. + * + * @param redisUri the connection URI. + * @return {@literal this} builder. + * @since 2.5.3 + */ + public LettuceClientConfigurationBuilder apply(RedisURI redisUri) { + + this.useSsl = redisUri.isSsl(); + this.verifyPeer = redisUri.isVerifyPeer(); + this.startTls = redisUri.isStartTls(); + + if (!redisUri.getTimeout().equals(RedisURI.DEFAULT_TIMEOUT_DURATION)) { + this.timeout = redisUri.getTimeout(); + } + + if (!ObjectUtils.isEmpty(redisUri.getClientName())) { + this.clientName = redisUri.getClientName(); + } + + return this; + } + /** * Enable SSL connections. * diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java index 9eca99886..cd2463f80 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java @@ -282,29 +282,51 @@ public class LettuceConnectionFactory } /** - * Creates a {@link RedisConfiguration} based on a {@link RedisURI} according to the following: + * Creates a {@link RedisConfiguration} based on a {@link String URI} according to the following: * * - * @param redisURI the connection info in the format of a RedisURI + * @param redisUri the connection URI in the format of a {@link RedisURI}. * @return an appropriate {@link RedisConfiguration} instance representing the Redis URI. + * @since 2.5.3 + * @see RedisURI */ - public static RedisConfiguration createRedisConfiguration(RedisURI redisURI) { + public static RedisConfiguration createRedisConfiguration(String redisUri) { - Assert.notNull(redisURI, "RedisURI must not be null"); + Assert.hasText(redisUri, "RedisURI must not be null and not empty"); - if (!ObjectUtils.isEmpty(redisURI.getSentinels())) { - return LettuceConverters.redisUriToSentinelConfiguration(redisURI); + return createRedisConfiguration(RedisURI.create(redisUri)); + } + + /** + * Creates a {@link RedisConfiguration} based on a {@link RedisURI} according to the following: + * + * + * @param redisUri the connection URI. + * @return an appropriate {@link RedisConfiguration} instance representing the Redis URI. + * @since 2.5.3 + * @see RedisURI + */ + public static RedisConfiguration createRedisConfiguration(RedisURI redisUri) { + + Assert.notNull(redisUri, "RedisURI must not be null"); + + if (!ObjectUtils.isEmpty(redisUri.getSentinels())) { + return LettuceConverters.createRedisSentinelConfiguration(redisUri); } - if (!ObjectUtils.isEmpty(redisURI.getSocket())) { - return LettuceConverters.redisUriToSocketConfiguration(redisURI); + if (!ObjectUtils.isEmpty(redisUri.getSocket())) { + return LettuceConverters.createRedisSocketConfiguration(redisUri); } - return LettuceConverters.redisUriToStandaloneConfiguration(redisURI); + return LettuceConverters.createRedisStandaloneConfiguration(redisUri); } /* diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java index e1740e68a..beb863a10 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java @@ -527,16 +527,51 @@ public abstract class LettuceConverters extends Converters { return builder.build(); } + /** + * Converts a {@link RedisURI} to its corresponding {@link RedisStandaloneConfiguration}. + * + * @param redisURI the uri containing the Redis connection info + * @return a {@link RedisStandaloneConfiguration} representing the connection information in the Redis URI. + * @since 2.5.3 + */ + static RedisStandaloneConfiguration createRedisStandaloneConfiguration(RedisURI redisURI) { + + RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration(); + standaloneConfiguration.setHostName(redisURI.getHost()); + standaloneConfiguration.setPort(redisURI.getPort()); + standaloneConfiguration.setDatabase(redisURI.getDatabase()); + + applyAuthentication(redisURI, standaloneConfiguration); + + return standaloneConfiguration; + } + + /** + * Converts a {@link RedisURI} to its corresponding {@link RedisSocketConfiguration}. + * + * @param redisURI the uri containing the Redis connection info using a local unix domain socket + * @return a {@link RedisSocketConfiguration} representing the connection information in the Redis URI. + * @since 2.5.3 + */ + static RedisSocketConfiguration createRedisSocketConfiguration(RedisURI redisURI) { + + RedisSocketConfiguration socketConfiguration = new RedisSocketConfiguration(); + socketConfiguration.setSocket(redisURI.getSocket()); + socketConfiguration.setDatabase(redisURI.getDatabase()); + + applyAuthentication(redisURI, socketConfiguration); + + return socketConfiguration; + } + /** * Converts a {@link RedisURI} to its corresponding {@link RedisSentinelConfiguration}. * * @param redisURI the uri containing the Redis Sentinel connection info * @return a {@link RedisSentinelConfiguration} representing the Redis Sentinel information in the Redis URI. - * @since 2.6 + * @since 2.5.3 */ - static RedisSentinelConfiguration redisUriToSentinelConfiguration(RedisURI redisURI) { - - Assert.notNull(redisURI, "RedisURI is required"); + static RedisSentinelConfiguration createRedisSentinelConfiguration(RedisURI redisURI) { RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration(); if (!ObjectUtils.isEmpty(redisURI.getSentinelMasterId())) { @@ -557,51 +592,12 @@ public abstract class LettuceConverters extends Converters { return sentinelConfiguration; } - /** - * Converts a {@link RedisURI} to its corresponding {@link RedisSocketConfiguration}. - * - * @param redisURI the uri containing the Redis connection info using a local unix domain socket - * @return a {@link RedisSocketConfiguration} representing the connection information in the Redis URI. - * @since 2.6 - */ - static RedisSocketConfiguration redisUriToSocketConfiguration(RedisURI redisURI) { - - Assert.notNull(redisURI, "RedisURI is required"); - - RedisSocketConfiguration socketConfiguration = new RedisSocketConfiguration(); - socketConfiguration.setSocket(redisURI.getSocket()); - socketConfiguration.setDatabase(redisURI.getDatabase()); - - applyAuthentication(redisURI, socketConfiguration); - - return socketConfiguration; - } - - /** - * Converts a {@link RedisURI} to its corresponding {@link RedisStandaloneConfiguration}. - * - * @param redisURI the uri containing the Redis connection info - * @return a {@link RedisStandaloneConfiguration} representing the connection information in the Redis URI. - * @since 2.6 - */ - static RedisStandaloneConfiguration redisUriToStandaloneConfiguration(RedisURI redisURI) { - - Assert.notNull(redisURI, "RedisURI is required"); - - RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration(); - standaloneConfiguration.setHostName(redisURI.getHost()); - standaloneConfiguration.setPort(redisURI.getPort()); - standaloneConfiguration.setDatabase(redisURI.getDatabase()); - - applyAuthentication(redisURI, standaloneConfiguration); - - return standaloneConfiguration; - } - private static void applyAuthentication(RedisURI redisURI, RedisConfiguration.WithAuthentication redisConfiguration) { + if (StringUtils.hasText(redisURI.getUsername())) { redisConfiguration.setUsername(redisURI.getUsername()); } + if (redisURI.getPassword() != null) { redisConfiguration.setPassword(redisURI.getPassword()); } diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java index da54f9a87..c196b5f51 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClientConfigurationUnitTests.java @@ -18,12 +18,13 @@ package org.springframework.data.redis.connection.lettuce; import static org.assertj.core.api.Assertions.*; import io.lettuce.core.ClientOptions; +import io.lettuce.core.RedisURI; import io.lettuce.core.TimeoutOptions; import io.lettuce.core.resource.ClientResources; -import org.junit.jupiter.api.Test; import java.time.Duration; +import org.junit.jupiter.api.Test; import org.springframework.data.redis.test.extension.LettuceTestClientResources; @@ -105,4 +106,29 @@ class LettuceClientConfigurationUnitTests { void clientConfigurationThrowsExceptionForEmptyClientName() { assertThatIllegalArgumentException().isThrownBy(() -> LettuceClientConfiguration.builder().clientName(" ")); } + + @Test // GH-2116 + void shouldApplySettingsFromRedisURI() { + + LettuceClientConfiguration configuration = LettuceClientConfiguration.builder() // + .apply(RedisURI.create("rediss://foo?verifyPeer=FULL&clientName=bar&timeout=10s")).build(); + + assertThat(configuration.isUseSsl()).isTrue(); + assertThat(configuration.isVerifyPeer()).isTrue(); + assertThat(configuration.isStartTls()).isFalse(); + assertThat(configuration.getClientName()).contains("bar"); + assertThat(configuration.getCommandTimeout()).isEqualTo(Duration.ofSeconds(10)); + } + + @Test // GH-2116 + void emptySettingsRetainClientConfiguration() { + + LettuceClientConfiguration configuration = LettuceClientConfiguration.builder() // + .clientName("hello") // + .commandTimeout(Duration.ofMillis(1)) // + .apply(RedisURI.create("rediss://foo")).build(); + + assertThat(configuration.getClientName()).contains("hello"); + assertThat(configuration.getCommandTimeout()).isEqualTo(Duration.ofMillis(1)); + } } diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryCreateRedisConfigurationUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryCreateRedisConfigurationUnitTests.java deleted file mode 100644 index e14f54df0..000000000 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryCreateRedisConfigurationUnitTests.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2015-2021 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.data.redis.connection.lettuce; - -import static org.assertj.core.api.Assertions.*; - -import io.lettuce.core.RedisURI; - -import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import org.springframework.data.redis.connection.RedisConfiguration; -import org.springframework.data.redis.connection.RedisNode; -import org.springframework.data.redis.connection.RedisSentinelConfiguration; -import org.springframework.data.redis.connection.RedisSocketConfiguration; -import org.springframework.data.redis.connection.RedisStandaloneConfiguration; - -/** - * Unit tests for the {@link LettuceConnectionFactory#createRedisConfiguration(RedisURI)} factory method. - * - * @author Chris Bono - */ -class LettuceConnectionFactoryCreateRedisConfigurationUnitTests { - - @Test - void requiresRedisURI() { - assertThatThrownBy(() -> LettuceConnectionFactory.createRedisConfiguration(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("RedisURI must not be null"); - } - - @Nested // GH-2117 - class CreateRedisSentinelConfiguration { - - @Test - void minimalFieldsSetOnRedisURI() { - RedisURI redisURI = RedisURI.create("redis-sentinel://myserver?sentinelMasterId=5150"); - RedisSentinelConfiguration expectedSentinelConfiguration = new RedisSentinelConfiguration(); - expectedSentinelConfiguration.setMaster("5150"); - expectedSentinelConfiguration.addSentinel(new RedisNode("myserver", 26379)); - - RedisConfiguration sentinelConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); - - assertThat(sentinelConfiguration).usingRecursiveComparison(sentinelCompareConfig()) - .isInstanceOf(RedisSentinelConfiguration.class) - .isEqualTo(expectedSentinelConfiguration); - } - - @Test - void allFieldsSetOnRedisURI() { - RedisURI redisURI = RedisURI.create("redis-sentinel://fooUser:fooPass@myserver1:111,myserver2:222/7?sentinelMasterId=5150"); - // Set the passwords directly on the sentinels so that it gets picked up by converter - char[] sentinelPass = "changeme".toCharArray(); - redisURI.getSentinels().forEach(sentinelRedisUri -> sentinelRedisUri.setPassword(sentinelPass)); - RedisSentinelConfiguration expectedSentinelConfiguration = new RedisSentinelConfiguration(); - expectedSentinelConfiguration.setMaster("5150"); - expectedSentinelConfiguration.setDatabase(7); - expectedSentinelConfiguration.setUsername("fooUser"); - expectedSentinelConfiguration.setPassword("fooPass"); - expectedSentinelConfiguration.setSentinelPassword(sentinelPass); - expectedSentinelConfiguration.addSentinel(new RedisNode("myserver1", 111)); - expectedSentinelConfiguration.addSentinel(new RedisNode("myserver2", 222)); - - RedisConfiguration sentinelConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); - - assertThat(sentinelConfiguration).usingRecursiveComparison(sentinelCompareConfig()) - .isInstanceOf(RedisSentinelConfiguration.class) - .isEqualTo(expectedSentinelConfiguration); - } - - // RedisSentinelConfiguration does not provide equals impl - private RecursiveComparisonConfiguration sentinelCompareConfig() { - return RecursiveComparisonConfiguration.builder().withComparedFields( - "master", - "username", - "password", - "sentinelPassword", - "database", - "sentinels") - .build(); - } - } - - @Nested // GH-2117 - class CreateRedisSocketConfiguration { - - @Test - void minimalFieldsSetOnRedisURI() { - RedisURI redisURI = RedisURI.builder() - .socket("mysocket") - .build(); - RedisSocketConfiguration expectedSocketConfiguration = new RedisSocketConfiguration(); - expectedSocketConfiguration.setSocket("mysocket"); - - RedisConfiguration socketConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); - - assertThat(socketConfiguration).usingRecursiveComparison(socketCompareConfig()) - .isInstanceOf(RedisSocketConfiguration.class) - .isEqualTo(expectedSocketConfiguration); - } - - @Test - void allFieldsSetOnRedisURI() { - RedisURI redisURI = RedisURI.builder() - .socket("mysocket") - .withAuthentication("fooUser", "fooPass".toCharArray()) - .withDatabase(7) - .build(); - RedisSocketConfiguration expectedSocketConfiguration = new RedisSocketConfiguration(); - expectedSocketConfiguration.setSocket("mysocket"); - expectedSocketConfiguration.setUsername("fooUser"); - expectedSocketConfiguration.setPassword("fooPass"); - expectedSocketConfiguration.setDatabase(7); - - RedisConfiguration socketConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); - - assertThat(socketConfiguration).usingRecursiveComparison(socketCompareConfig()) - .isInstanceOf(RedisSocketConfiguration.class) - .isEqualTo(expectedSocketConfiguration); - } - - // RedisSocketConfiguration does not provide equals impl - private RecursiveComparisonConfiguration socketCompareConfig() { - return RecursiveComparisonConfiguration.builder().withComparedFields( - "socket", - "username", - "password", - "database") - .build(); - } - } - - @Nested // GH-2117 - class CreateRedisStandaloneConfiguration { - - @Test - void minimalFieldsSetOnRedisURI() { - RedisURI redisURI = RedisURI.create("redis://myserver"); - RedisStandaloneConfiguration expectedStandaloneConfiguration = new RedisStandaloneConfiguration(); - expectedStandaloneConfiguration.setHostName("myserver"); - - RedisConfiguration StandaloneConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); - - assertThat(StandaloneConfiguration).usingRecursiveComparison(standaloneCompareConfig()) - .isInstanceOf(RedisStandaloneConfiguration.class) - .isEqualTo(expectedStandaloneConfiguration); - } - - @Test - void allFieldsSetOnRedisURI() { - RedisURI redisURI = RedisURI.create("redis://fooUser:fooPass@myserver1:111/7"); - RedisStandaloneConfiguration expectedStandaloneConfiguration = new RedisStandaloneConfiguration(); - expectedStandaloneConfiguration.setHostName("myserver1"); - expectedStandaloneConfiguration.setPort(111); - expectedStandaloneConfiguration.setDatabase(7); - expectedStandaloneConfiguration.setUsername("fooUser"); - expectedStandaloneConfiguration.setPassword("fooPass"); - - RedisConfiguration StandaloneConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); - - assertThat(StandaloneConfiguration).usingRecursiveComparison(standaloneCompareConfig()) - .isInstanceOf(RedisStandaloneConfiguration.class) - .isEqualTo(expectedStandaloneConfiguration); - - } - - // RedisStandaloneConfiguration does not provide equals impl - private RecursiveComparisonConfiguration standaloneCompareConfig() { - return RecursiveComparisonConfiguration.builder().withComparedFields( - "host", - "port", - "username", - "password", - "database") - .build(); - } - } - -} diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryRedisURITests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryRedisURITests.java new file mode 100644 index 000000000..2e0c86aa1 --- /dev/null +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryRedisURITests.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015-2021 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.data.redis.connection.lettuce; + +import io.lettuce.core.RedisURI; + +/** + * Unit tests for the {@link LettuceConnectionFactory#createRedisConfiguration(RedisURI)} factory method. + * + * @author Chris Bono + */ +class LettuceConnectionFactoryRedisURITests { + + + +} diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java index 3a36f849d..6e6472828 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryUnitTests.java @@ -56,6 +56,7 @@ import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisClusterConnection; import org.springframework.data.redis.connection.RedisConfiguration; import org.springframework.data.redis.connection.RedisConnection; +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.RedisSocketConfiguration; @@ -72,6 +73,7 @@ import org.springframework.test.util.ReflectionTestUtils; * @author Ruben Cervilla * @author Luis De Bello * @author Andrea Como + * @author Chris Bono */ class LettuceConnectionFactoryUnitTests { @@ -1026,6 +1028,110 @@ class LettuceConnectionFactoryUnitTests { assertThatIllegalStateException().isThrownBy(connectionFactory::getReactiveClusterConnection); } + @Test // GH-2116 + void createRedisConfigurationRequiresRedisUri() { + assertThatIllegalArgumentException() + .isThrownBy(() -> LettuceConnectionFactory.createRedisConfiguration((RedisURI) null)) + .withMessage("RedisURI must not be null"); + } + + @Test // GH-2116 + void createMinimalRedisStandaloneConfiguration() { + + RedisURI redisURI = RedisURI.create("redis://myserver"); + + RedisStandaloneConfiguration expected = new RedisStandaloneConfiguration(); + expected.setHostName("myserver"); + + RedisConfiguration configuration = LettuceConnectionFactory.createRedisConfiguration(redisURI); + + assertThat(configuration).isEqualTo(expected); + } + + @Test // GH-2116 + void createFullRedisStanaloneConfiguration() { + + RedisURI redisURI = RedisURI.create("redis://fooUser:fooPass@myserver1:111/7"); + + RedisStandaloneConfiguration expected = new RedisStandaloneConfiguration(); + expected.setHostName("myserver1"); + expected.setPort(111); + expected.setDatabase(7); + expected.setUsername("fooUser"); + expected.setPassword("fooPass"); + + RedisConfiguration configuration = LettuceConnectionFactory.createRedisConfiguration(redisURI); + + assertThat(configuration).isEqualTo(expected); + } + + @Test // GH-2116 + void createMinimalRedisSocketConfiguration() { + + RedisURI redisURI = RedisURI.Builder.socket("mysocket").build(); + + RedisSocketConfiguration expected = new RedisSocketConfiguration(); + expected.setSocket("mysocket"); + + RedisConfiguration socketConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); + + assertThat(socketConfiguration).isEqualTo(expected); + } + + @Test // GH-2116 + void createFullRedisSocketConfiguration() { + + RedisURI redisURI = RedisURI.Builder.socket("mysocket").withAuthentication("fooUser", "fooPass".toCharArray()) + .withDatabase(7).build(); + + RedisSocketConfiguration expected = new RedisSocketConfiguration(); + expected.setSocket("mysocket"); + expected.setUsername("fooUser"); + expected.setPassword("fooPass"); + expected.setDatabase(7); + + RedisConfiguration socketConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); + + assertThat(socketConfiguration).isEqualTo(expected); + } + + @Test // GH-2116 + void createMinimalRedisSentinelConfiguration() { + + RedisURI redisURI = RedisURI.create("redis-sentinel://myserver?sentinelMasterId=5150"); + + RedisSentinelConfiguration expected = new RedisSentinelConfiguration(); + expected.setMaster("5150"); + expected.addSentinel(new RedisNode("myserver", 26379)); + + RedisConfiguration sentinelConfiguration = LettuceConnectionFactory.createRedisConfiguration(redisURI); + + assertThat(sentinelConfiguration).isEqualTo(expected); + } + + @Test // GH-2116 + void createFullRedisSentinelConfiguration() { + + RedisURI redisURI = RedisURI + .create("redis-sentinel://fooUser:fooPass@myserver1:111,myserver2:222/7?sentinelMasterId=5150"); + // Set the passwords directly on the sentinels so that it gets picked up by converter + char[] sentinelPass = "changeme".toCharArray(); + redisURI.getSentinels().forEach(sentinelRedisUri -> sentinelRedisUri.setPassword(sentinelPass)); + + RedisSentinelConfiguration expected = new RedisSentinelConfiguration(); + expected.setMaster("5150"); + expected.setDatabase(7); + expected.setUsername("fooUser"); + expected.setPassword("fooPass"); + expected.setSentinelPassword(sentinelPass); + expected.addSentinel(new RedisNode("myserver1", 111)); + expected.addSentinel(new RedisNode("myserver2", 222)); + + RedisConfiguration configuration = LettuceConnectionFactory.createRedisConfiguration(redisURI); + + assertThat(configuration).isEqualTo(expected); + } + @Data @AllArgsConstructor static class CustomRedisConfiguration implements RedisConfiguration, WithHostAndPort {