diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java index 50fd68211e..683c0ec55a 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/DefaultJedisClientConfiguration.java @@ -34,6 +34,7 @@ */ class DefaultJedisClientConfiguration implements JedisClientConfiguration { + private final Optional customizer; private final boolean useSsl; private final Optional sslSocketFactory; private final Optional sslParameters; @@ -44,11 +45,13 @@ class DefaultJedisClientConfiguration implements JedisClientConfiguration { private final Duration readTimeout; private final Duration connectTimeout; - DefaultJedisClientConfiguration(boolean useSsl, @Nullable SSLSocketFactory sslSocketFactory, + DefaultJedisClientConfiguration(@Nullable JedisClientConfigBuilderCustomizer customizer, boolean useSsl, + @Nullable SSLSocketFactory sslSocketFactory, @Nullable SSLParameters sslParameters, @Nullable HostnameVerifier hostnameVerifier, boolean usePooling, @Nullable GenericObjectPoolConfig poolConfig, @Nullable String clientName, Duration readTimeout, Duration connectTimeout) { + this.customizer = Optional.ofNullable(customizer); this.useSsl = useSsl; this.sslSocketFactory = Optional.ofNullable(sslSocketFactory); this.sslParameters = Optional.ofNullable(sslParameters); @@ -60,6 +63,11 @@ class DefaultJedisClientConfiguration implements JedisClientConfiguration { this.connectTimeout = connectTimeout; } + @Override + public Optional getCustomizer() { + return customizer; + } + @Override public boolean isUseSsl() { return useSsl; diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java new file mode 100644 index 0000000000..8f508c374f --- /dev/null +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfigBuilderCustomizer.java @@ -0,0 +1,39 @@ +/* + * Copyright 2024 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.jedis; + +import redis.clients.jedis.DefaultJedisClientConfig; + +/** + * Strategy interface for customizing {@link DefaultJedisClientConfig.Builder JedisClientConfig}. Any ClientConfig will + * be used to call this interface implementation so you can set the protocol, client name, etc. after Spring has applies + * its defaults. + * + * @author Mark Paluch + * @since 3.4 + * @see redis.clients.jedis.DefaultJedisClientConfig.Builder + */ +@FunctionalInterface +public interface JedisClientConfigBuilderCustomizer { + + /** + * Customize the {@link DefaultJedisClientConfig.Builder}. + * + * @param builder the builder to customize. + */ + void customize(DefaultJedisClientConfig.Builder builder); + +} diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java index 1c9628db0c..b6b9a22467 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClientConfiguration.java @@ -58,6 +58,12 @@ */ public interface JedisClientConfiguration { + /** + * @return the optional {@link JedisClientConfigBuilderCustomizer}. + * @since 3.4 + */ + Optional getCustomizer(); + /** * @return {@literal true} to use SSL, {@literal false} to use unencrypted connections. */ @@ -119,6 +125,8 @@ static JedisClientConfigurationBuilder builder() { /** * Creates a default {@link JedisClientConfiguration}. *
+ *
Customizer
+ *
none
*
SSL enabled
*
no
*
Pooling enabled
@@ -142,6 +150,15 @@ static JedisClientConfiguration defaultConfiguration() { */ interface JedisClientConfigurationBuilder { + /** + * Configure a {@link JedisClientConfigBuilderCustomizer} to configure + * {@link redis.clients.jedis.JedisClientConfig}. + * + * @return {@link JedisClientConfigurationBuilder}. + * @since 3.4 + */ + JedisClientConfigurationBuilder customize(JedisClientConfigBuilderCustomizer customizer); + /** * Enable SSL connections. * @@ -269,6 +286,7 @@ interface JedisSslClientConfigurationBuilder { class DefaultJedisClientConfigurationBuilder implements JedisClientConfigurationBuilder, JedisPoolingClientConfigurationBuilder, JedisSslClientConfigurationBuilder { + private @Nullable JedisClientConfigBuilderCustomizer customizer; private boolean useSsl; private @Nullable SSLSocketFactory sslSocketFactory; private @Nullable SSLParameters sslParameters; @@ -281,6 +299,15 @@ class DefaultJedisClientConfigurationBuilder implements JedisClientConfiguration private DefaultJedisClientConfigurationBuilder() {} + @Override + public JedisClientConfigurationBuilder customize(JedisClientConfigBuilderCustomizer customizer) { + + Assert.notNull(customizer, "JedisClientConfigBuilderCustomizer must not be null"); + + this.customizer = customizer; + return this; + } + @Override public JedisSslClientConfigurationBuilder useSsl() { @@ -366,8 +393,8 @@ public JedisClientConfigurationBuilder connectTimeout(Duration connectTimeout) { @Override public JedisClientConfiguration build() { - return new DefaultJedisClientConfiguration(useSsl, sslSocketFactory, sslParameters, hostnameVerifier, usePooling, - poolConfig, clientName, readTimeout, connectTimeout); + return new DefaultJedisClientConfiguration(customizer, useSsl, sslSocketFactory, sslParameters, hostnameVerifier, + usePooling, poolConfig, clientName, readTimeout, connectTimeout); } } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java index 21bd50df5e..c2ecc3e2cf 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java @@ -711,6 +711,8 @@ private JedisClientConfig createClientConfig(int database, @Nullable String user this.clientConfiguration.getSslParameters().ifPresent(builder::sslParameters); } + this.clientConfiguration.getCustomizer().ifPresent(customizer -> customizer.customize(builder)); + return builder.build(); } @@ -1087,6 +1089,11 @@ public static JedisClientConfiguration create(GenericObjectPoolConfig jedisPoolC return configuration; } + @Override + public Optional getCustomizer() { + return Optional.empty(); + } + @Override public boolean isUseSsl() { return useSsl; diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java index f6e3f3f9e4..b553cf4415 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactoryUnitTests.java @@ -18,9 +18,11 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPoolConfig; +import redis.clients.jedis.RedisProtocol; import java.io.IOException; import java.security.NoSuchAlgorithmException; @@ -338,6 +340,30 @@ void afterPropertiesTriggersConnectionInitialization() { assertThat(connectionFactory.isRunning()).isTrue(); } + @Test // GH-3007 + void clientConfigurationAppliesCustomizer() { + + JedisClientConfig resp3Config = apply( + JedisClientConfiguration.builder().customize(DefaultJedisClientConfig.Builder::resp3).build()); + + assertThat(resp3Config.getRedisProtocol()).isEqualTo(RedisProtocol.RESP3); + + JedisClientConfig resp2Config = apply( + JedisClientConfiguration.builder().customize(it -> it.protocol(RedisProtocol.RESP2)).build()); + + assertThat(resp2Config.getRedisProtocol()).isEqualTo(RedisProtocol.RESP2); + } + + private static JedisClientConfig apply(JedisClientConfiguration configuration) { + + JedisConnectionFactory connectionFactory = new JedisConnectionFactory(new RedisStandaloneConfiguration(), + configuration); + connectionFactory.setEarlyStartup(false); + connectionFactory.afterPropertiesSet(); + + return (JedisClientConfig) ReflectionTestUtils.getField(connectionFactory, "clientConfig"); + } + @Test // GH-2866 void earlyStartupDoesNotStartConnectionFactory() {