From 08cf2576f513e44381abe40c7fccc15f618386b3 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 3 Jan 2023 17:35:14 +0800 Subject: [PATCH 01/38] init --- eng/jacoco-test-coverage/pom.xml | 5 + eng/versioning/external_dependencies.txt | 3 + eng/versioning/version_client.txt | 1 + .../spring-cloud-azure-dependencies/pom.xml | 5 + .../azure-identity-extensions/pom.xml | 8 + .../implementation/enums/AuthProperty.java | 2 +- sdk/spring/ci.yml | 12 + sdk/spring/pom.xml | 2 + .../spring-cloud-azure-autoconfigure/pom.xml | 11 +- .../jedis/AzureJedisConnection.java | 20 + .../jedis/AzureJedisConnectionFactory.java | 266 +++++++++ ...isPasswordlessConnectionConfiguration.java | 241 ++++++++ .../jedis/AzureRedisCredentialSupplier.java | 35 ++ .../jedis/RedisUrlSyntaxException.java | 28 + .../main/resources/META-INF/spring.factories | 3 +- ...edisPasswordlessAutoConfigurationTest.java | 227 ++++++++ sdk/spring/spring-cloud-azure-service/pom.xml | 10 +- .../AzurePasswordlessProperties.java | 84 +++ .../AzureRedisPasswordlessProperties.java | 49 ++ .../service/redis/AzureJedisClientConfig.java | 221 ++++++++ .../service/redis/AzureJedisFactory.java | 111 ++++ .../cloud/service/redis/AzureJedisPool.java | 18 + .../cloud/service/redis/package-info.java | 4 + .../service/redis/AzureJedisPoolTest.java | 533 ++++++++++++++++++ .../spring-cloud-azure-starter-redis/pom.xml | 199 +++++++ 25 files changed, 2094 insertions(+), 4 deletions(-) create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnection.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java create mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java create mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisClientConfig.java create mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisFactory.java create mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisPool.java create mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/package-info.java create mode 100644 sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/redis/AzureJedisPoolTest.java create mode 100644 sdk/spring/spring-cloud-azure-starter-redis/pom.xml diff --git a/eng/jacoco-test-coverage/pom.xml b/eng/jacoco-test-coverage/pom.xml index 2ceba01c440a..3cbfcdef5bf1 100644 --- a/eng/jacoco-test-coverage/pom.xml +++ b/eng/jacoco-test-coverage/pom.xml @@ -547,6 +547,11 @@ spring-cloud-azure-starter-jdbc-postgresql 4.6.0-beta.1 + + com.azure.spring + spring-cloud-azure-starter-redis + 4.6.0-beta.1 + com.azure.spring spring-cloud-azure-starter-keyvault-certificates diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index c5bdf2eb5e2c..778d3d0ab3b0 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -398,3 +398,6 @@ storage_com.microsoft.azure:azure-storage;8.4.0 # sdk\appconfiguration\azure-spring-cloud-test-appconfiguration-config\pom.xml spring_com.microsoft.azure:azure;1.34.0 + +# sdk/spring/spring-cloud-azure-service/pom.xml +spring_redis.clients:jedis;3.8.0 \ No newline at end of file diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index af1766ac9121..59a283e38665 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -206,6 +206,7 @@ com.azure.spring:spring-cloud-azure-starter-data-cosmos;4.5.0;4.6.0-beta.1 com.azure.spring:spring-cloud-azure-starter-eventhubs;4.5.0;4.6.0-beta.1 com.azure.spring:spring-cloud-azure-starter-jdbc-mysql;4.5.0;4.6.0-beta.1 com.azure.spring:spring-cloud-azure-starter-jdbc-postgresql;4.5.0;4.6.0-beta.1 +com.azure.spring:spring-cloud-azure-starter-redis;4.6.0-beta.1;4.6.0-beta.1 com.azure.spring:spring-cloud-azure-starter-keyvault;4.5.0;4.6.0-beta.1 com.azure.spring:spring-cloud-azure-starter-keyvault-certificates;4.5.0;4.6.0-beta.1 com.azure.spring:spring-cloud-azure-starter-keyvault-secrets;4.5.0;4.6.0-beta.1 diff --git a/sdk/boms/spring-cloud-azure-dependencies/pom.xml b/sdk/boms/spring-cloud-azure-dependencies/pom.xml index 5c38b68eff9d..272b9beeba78 100644 --- a/sdk/boms/spring-cloud-azure-dependencies/pom.xml +++ b/sdk/boms/spring-cloud-azure-dependencies/pom.xml @@ -303,6 +303,11 @@ spring-cloud-azure-starter-jdbc-postgresql ${project.version} + + com.azure.spring + spring-cloud-azure-starter-redis + ${project.version} + diff --git a/sdk/identity/azure-identity-extensions/pom.xml b/sdk/identity/azure-identity-extensions/pom.xml index b50776f978e3..3b8e5fd8d440 100644 --- a/sdk/identity/azure-identity-extensions/pom.xml +++ b/sdk/identity/azure-identity-extensions/pom.xml @@ -92,6 +92,14 @@ false + + org.apache.maven.plugins + maven-compiler-plugin + + 9 + 9 + + diff --git a/sdk/identity/azure-identity-extensions/src/main/java/com/azure/identity/extensions/implementation/enums/AuthProperty.java b/sdk/identity/azure-identity-extensions/src/main/java/com/azure/identity/extensions/implementation/enums/AuthProperty.java index f7a39a37ece0..c8f3806cd1da 100644 --- a/sdk/identity/azure-identity-extensions/src/main/java/com/azure/identity/extensions/implementation/enums/AuthProperty.java +++ b/sdk/identity/azure-identity-extensions/src/main/java/com/azure/identity/extensions/implementation/enums/AuthProperty.java @@ -83,7 +83,7 @@ public enum AuthProperty { "Scopes for Azure resources.", false), /** - * Max time to get an access token. + * Max time to get an access token.SCOPES */ GET_TOKEN_TIMEOUT("azure.accessTokenTimeoutInSeconds", "Max time to get an access token.", diff --git a/sdk/spring/ci.yml b/sdk/spring/ci.yml index 4eaa6bf5d9d2..875bdcd397ee 100644 --- a/sdk/spring/ci.yml +++ b/sdk/spring/ci.yml @@ -199,6 +199,10 @@ parameters: displayName: 'spring-cloud-azure-starter-jdbc-postgresql' type: boolean default: true +- name: release_springcloudazurestarterredis + displayName: 'spring-cloud-azure-starter-redis' + type: boolean + default: true extends: template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml @@ -513,3 +517,11 @@ extends: skipUpdatePackageJson: true skipVerifyChangelog: true releaseInBatch: ${{ parameters.release_springcloudazurestarterjdbcpostgresql }} + - name: spring-cloud-azure-starter-jdbc-postgresql + groupId: com.azure.spring + safeName: springcloudazurestarterredis + skipPublishDocGithubIo: true + skipPublishDocMs: true + skipUpdatePackageJson: true + skipVerifyChangelog: true + releaseInBatch: ${{ parameters.release_springcloudazurestarterredis }} diff --git a/sdk/spring/pom.xml b/sdk/spring/pom.xml index 17506f30ac56..c9dc74ffd290 100644 --- a/sdk/spring/pom.xml +++ b/sdk/spring/pom.xml @@ -58,6 +58,7 @@ spring-cloud-azure-trace-sleuth spring-cloud-azure-starter-jdbc-mysql spring-cloud-azure-starter-jdbc-postgresql + spring-cloud-azure-starter-redis spring-cloud-azure-integration-tests @@ -110,6 +111,7 @@ spring-cloud-azure-trace-sleuth spring-cloud-azure-starter-jdbc-mysql spring-cloud-azure-starter-jdbc-postgresql + spring-cloud-azure-starter-redis diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml b/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml index 741664919375..428ad4a19736 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml +++ b/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml @@ -106,6 +106,7 @@ 2.7.6 true + com.azure @@ -281,7 +282,7 @@ com.azure azure-identity-extensions - 1.0.0 + 1.1.0-beta.1 true @@ -320,6 +321,14 @@ test + + + redis.clients + jedis + 3.8.0 + true + + com.mysql diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnection.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnection.java new file mode 100644 index 000000000000..eb00bfe825b5 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnection.java @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; + +import org.springframework.data.redis.connection.jedis.JedisConnection; +import org.springframework.lang.Nullable; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.util.Pool; + +class AzureJedisConnection extends JedisConnection { + + protected AzureJedisConnection(Jedis jedis, + @Nullable Pool pool, + JedisClientConfig nodeConfig, + JedisClientConfig sentinelConfig) { + super(jedis, pool, nodeConfig, sentinelConfig); + } +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java new file mode 100644 index 000000000000..5c797a044a0a --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -0,0 +1,266 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; + +import com.azure.spring.cloud.service.redis.AzureJedisClientConfig; +import com.azure.spring.cloud.service.redis.AzureJedisPool; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.RedisConnectionFailureException; +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.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisPassword; +import org.springframework.data.redis.connection.RedisSentinelConnection; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; +import org.springframework.data.redis.connection.jedis.JedisConnection; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.util.Pool; + +import java.util.function.Supplier; + +public class AzureJedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(AzureJedisConnectionFactory.class); + + private RedisStandaloneConfiguration standaloneConfig; + + private final Supplier credentialSupplier; + private final JedisClientConfiguration clientConfiguration; + private JedisClientConfig jedisClientConfig; + private @Nullable Pool pool; + private boolean initialized; + private boolean destroyed; + + private boolean convertPipelineAndTxResults = true; + + + public AzureJedisConnectionFactory(RedisStandaloneConfiguration standaloneConfig, JedisClientConfiguration clientConfiguration, Supplier credentialSupplier) { + this.standaloneConfig = standaloneConfig; + this.clientConfiguration = clientConfiguration; + this.credentialSupplier = credentialSupplier; + } + + @Override + public void afterPropertiesSet() { + this.jedisClientConfig = createClientConfig(this.standaloneConfig, this.clientConfiguration); + + if (getUsePool()) { + this.pool = createRedisPool(); + } + this.initialized = true; + } + + @Override + public RedisClusterConnection getClusterConnection() { + throw new UnsupportedOperationException("This operation is not supported"); + } + + @Override + public boolean getConvertPipelineAndTxResults() { + return this.convertPipelineAndTxResults; + } + + @Override + public RedisSentinelConnection getSentinelConnection() { + throw new UnsupportedOperationException("This operation is not supported"); + } + + @Override + public void destroy() { + if (getUsePool() && pool != null) { + + try { + pool.destroy(); + } catch (Exception ex) { + LOGGER.warn("Cannot properly close Jedis pool", ex); + } + pool = null; + } + + this.destroyed = true; + } + + @Override + public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + return null; + } + + @Override + public RedisConnection getConnection() { + assertInitialized(); + + Jedis jedis = fetchJedisConnector(); + + JedisClientConfig sentinelConfig = this.jedisClientConfig; + + JedisConnection connection = (getUsePool() ? new AzureJedisConnection(jedis, pool, this.jedisClientConfig, sentinelConfig) + : new AzureJedisConnection(jedis, null, this.jedisClientConfig, sentinelConfig)); + connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults); + return postProcessConnection(connection); + } + + /** + * Specifies if pipelined results should be converted to the expected data type. If false, results of + * {@link JedisConnection#closePipeline()} and {@link JedisConnection#exec()} will be of the type returned by the + * Jedis driver. + * + * @param convertPipelineAndTxResults Whether or not to convert pipeline and tx results. + */ + public void setConvertPipelineAndTxResults(boolean convertPipelineAndTxResults) { + this.convertPipelineAndTxResults = convertPipelineAndTxResults; + } + + public boolean getUsePool() { + return clientConfiguration.isUsePooling(); + } + + public int getDatabase() { + return RedisConfiguration.getDatabaseOrElse(standaloneConfig, standaloneConfig::getDatabase); + } + + public int getPort() { + return standaloneConfig.getPort(); + } + + public boolean isUseSsl() { + return clientConfiguration.isUseSsl(); + } + + /** + * Returns the Redis hostname. + * + * @return the hostName. + */ + public String getHostName() { + return standaloneConfig.getHostName(); + } + + /** + * Returns the password used for authenticating with the Redis server. + * + * @return password for authentication. + */ + @Nullable + public String getPassword() { + return getRedisPassword().map(String::new).orElse(null); + } + + // TODO how to not use @SuppressWarnings + @SuppressWarnings("unchecked") + @Nullable + public GenericObjectPoolConfig getPoolConfig() { + return clientConfiguration.getPoolConfig().orElse(null); + } + + public JedisClientConfiguration getClientConfiguration() { + return clientConfiguration; + } + + @Nullable + public String getClientName() { + return clientConfiguration.getClientName().orElse(null); + } + + /** + * Returns the timeout. + * + * @return the timeout. + */ + public int getTimeout() { + return getReadTimeout(); + } + + protected JedisConnection postProcessConnection(JedisConnection connection) { + return connection; + } + + @SuppressWarnings("unchecked") + protected Pool createRedisPool() { + return new AzureJedisPool(this.clientConfiguration.getPoolConfig().get(), + new HostAndPort(this.standaloneConfig.getHostName(), this.standaloneConfig.getPort()), + this.jedisClientConfig); + } + + protected Jedis fetchJedisConnector() { + try { + + if (getUsePool() && pool != null) { + LOGGER.info("Get connection from pool."); + return pool.getResource(); + } + + Jedis jedis = createJedis(); + // force initialization (see Jedis issue #82) + jedis.connect(); + + return jedis; + } catch (Exception ex) { + throw new RedisConnectionFailureException("Cannot get Jedis connection", ex); + } + } + + private Jedis createJedis() { + return new Jedis(new HostAndPort(this.standaloneConfig.getHostName(), this.standaloneConfig.getPort()), this.jedisClientConfig); + } + + private JedisClientConfig createClientConfig(RedisStandaloneConfiguration standaloneConfig, + JedisClientConfiguration clientConfig) { + + String username = standaloneConfig.getUsername(); + RedisPassword password = standaloneConfig.getPassword(); + + AzureJedisClientConfig.Builder builder = AzureJedisClientConfig.builder(); + + clientConfig.getClientName().ifPresent(builder::clientName); + builder.connectionTimeoutMillis(Math.toIntExact(clientConfig.getConnectTimeout().toMillis())); + builder.socketTimeoutMillis(Math.toIntExact(clientConfig.getReadTimeout().toMillis())); + + builder.database(standaloneConfig.getDatabase()); + + if (!ObjectUtils.isEmpty(username)) { + builder.user(username); + } + + password.toOptional().map(String::new).ifPresent(builder::password); + + builder.credentialSupplier(credentialSupplier); + + if (clientConfig.isUseSsl()) { + + builder.ssl(true); + + clientConfig.getSslSocketFactory().ifPresent(builder::sslSocketFactory); + clientConfig.getHostnameVerifier().ifPresent(builder::hostnameVerifier); + clientConfig.getSslParameters().ifPresent(builder::sslParameters); + } + + return builder.build(); + } + + private int getReadTimeout() { + return Math.toIntExact(clientConfiguration.getReadTimeout().toMillis()); + } + + private RedisPassword getRedisPassword() { + return RedisConfiguration.getPasswordOrElse(this.standaloneConfig, standaloneConfig::getPassword); + } + + private void assertInitialized() { + Assert.state(this.initialized, "JedisConnectionFactory was not initialized through afterPropertiesSet()"); + Assert.state(!this.destroyed, "JedisConnectionFactory was destroyed and cannot be used anymore"); + } + +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java new file mode 100644 index 000000000000..12c8948e099c --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java @@ -0,0 +1,241 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; + +import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisPassword; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; +import org.springframework.data.redis.connection.jedis.JedisConnection; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPoolConfig; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Properties; +import java.util.function.Supplier; + +import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreNull; +import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreTargetNonNull; + +/** + * Azure Redis passwordless connection configuration using Jedis. + * + * @since 4.6.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass({GenericObjectPool.class, JedisConnection.class, Jedis.class}) +@ConditionalOnExpression("${spring.redis.azure.passwordless-enabled:false}") +@ConditionalOnMissingBean(RedisConnectionFactory.class) +@ConditionalOnProperty(prefix = "spring.redis", name = { "host", "username" }) +@EnableConfigurationProperties(RedisProperties.class) +class AzureJedisPasswordlessConnectionConfiguration { + + private static final boolean COMMONS_POOL2_AVAILABLE = ClassUtils.isPresent("org.apache.commons.pool2.ObjectPool", + AzureJedisPasswordlessConnectionConfiguration.class.getClassLoader()); + + private static final String AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME = "azureRedisCredentialSupplier"; + + private static final int AZURE_REDIS_PORT = 6380; + + @Bean + @ConfigurationProperties(prefix = "spring.redis.azure") + AzureRedisPasswordlessProperties redisPasswordlessProperties() { + return new AzureRedisPasswordlessProperties(); + } + + @Bean(name = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) + @ConditionalOnMissingBean(name = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) + Supplier azureRedisCredentialSupplier(ObjectProvider azureGlobalProperties, AzureRedisPasswordlessProperties azureRedisPasswordlessProperties) { + Properties properties = mergeAzureProperties(azureGlobalProperties.getIfAvailable(), azureRedisPasswordlessProperties).toProperties(); + return new AzureRedisCredentialSupplier(properties); + } + + @Bean + AzureJedisConnectionFactory azureRedisConnectionFactory(RedisProperties redisProperties, @Qualifier(value = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) Supplier azureRedisCredentialSupplier) { + RedisStandaloneConfiguration standaloneConfig = getStandaloneConfig(redisProperties); + JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(redisProperties); + + return new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration , azureRedisCredentialSupplier); + } + + + private JedisClientConfiguration getJedisClientConfiguration(RedisProperties redisProperties) { + + JedisClientConfiguration.JedisClientConfigurationBuilder builder = applyProperties(redisProperties, JedisClientConfiguration.builder()); + builder.useSsl(); + RedisProperties.Pool pool = redisProperties.getJedis().getPool(); + + if (isPoolEnabled(pool)) { + applyPooling(pool, builder); + } + + if (StringUtils.hasText(redisProperties.getUrl())) { + customizeConfigurationFromUrl(redisProperties, builder); + } + return builder.build(); + } + + private JedisClientConfiguration.JedisClientConfigurationBuilder applyProperties(RedisProperties properties, JedisClientConfiguration.JedisClientConfigurationBuilder builder) { + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(properties.getTimeout()).to(builder::readTimeout); + map.from(properties.getConnectTimeout()).to(builder::connectTimeout); + map.from(properties.getClientName()).whenHasText().to(builder::clientName); + return builder; + } + + private void customizeConfigurationFromUrl(RedisProperties redisProperties, JedisClientConfiguration.JedisClientConfigurationBuilder builder) { + ConnectionInfo connectionInfo = parseUrl(redisProperties.getUrl()); + if (connectionInfo.isUseSsl()) { + builder.useSsl(); + } + } + + private boolean isPoolEnabled(RedisProperties.Pool pool) { + Boolean enabled = pool.getEnabled(); + return (enabled != null) ? enabled : COMMONS_POOL2_AVAILABLE; + } + + private void applyPooling(RedisProperties.Pool pool, + JedisClientConfiguration.JedisClientConfigurationBuilder builder) { + builder.usePooling().poolConfig(jedisPoolConfig(pool)); + } + + private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) { + JedisPoolConfig config = new JedisPoolConfig(); + config.setMaxTotal(pool.getMaxActive()); + config.setMaxIdle(pool.getMaxIdle()); + config.setMinIdle(pool.getMinIdle()); + if (pool.getTimeBetweenEvictionRuns() != null) { + config.setTimeBetweenEvictionRuns(pool.getTimeBetweenEvictionRuns()); + } + if (pool.getMaxWait() != null) { + config.setMaxWait(pool.getMaxWait()); + } + return config; + } + + private AzureRedisPasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureRedisPasswordlessProperties redisPasswordlessProperties) { + AzureRedisPasswordlessProperties target = new AzureRedisPasswordlessProperties(); + copyPropertiesIgnoreNull(redisPasswordlessProperties.getScopes(), target.getScopes()); + copyPropertiesIgnoreNull(redisPasswordlessProperties.getCredential(), target.getCredential()); + copyPropertiesIgnoreNull(redisPasswordlessProperties.getProfile(), target.getProfile()); + copyPropertiesIgnoreNull(redisPasswordlessProperties.getClient(), target.getClient()); + copyPropertiesIgnoreNull(redisPasswordlessProperties.getProxy(), target.getProxy()); + + if (azureGlobalProperties != null) { + copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getProfile(), target.getProfile()); + copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getCredential(), target.getCredential()); + } + + return redisPasswordlessProperties; + } + + private RedisStandaloneConfiguration getStandaloneConfig(RedisProperties redisProperties) { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); + if (StringUtils.hasText(redisProperties.getUrl())) { + ConnectionInfo connectionInfo = parseUrl(redisProperties.getUrl()); + config.setHostName(connectionInfo.getHostName()); + config.setPort(connectionInfo.getPort()); + config.setUsername(connectionInfo.getUsername()); + config.setPassword(RedisPassword.of(connectionInfo.getPassword())); + } + else { + config.setHostName(redisProperties.getHost()); + config.setPort(redisProperties.getPort()); + config.setUsername(redisProperties.getUsername()); + config.setPassword(RedisPassword.of(redisProperties.getPassword())); + } + config.setDatabase(redisProperties.getDatabase()); + if (config.getPort() == 0) { + config.setPort(AZURE_REDIS_PORT); + } + return config; + } + + ConnectionInfo parseUrl(String url) { + try { + URI uri = new URI(url); + String scheme = uri.getScheme(); + if (!"redis".equals(scheme) && !"rediss".equals(scheme)) { + throw new RedisUrlSyntaxException(url); + } + boolean useSsl = ("rediss".equals(scheme)); + String username = null; + String password = null; + if (uri.getUserInfo() != null) { + String candidate = uri.getUserInfo(); + int index = candidate.indexOf(':'); + if (index >= 0) { + username = candidate.substring(0, index); + password = candidate.substring(index + 1); + } + else { + password = candidate; + } + } + return new ConnectionInfo(uri, useSsl, username, password); + } + catch (URISyntaxException ex) { + throw new RedisUrlSyntaxException(url, ex); + } + } + + static class ConnectionInfo { + + private final URI uri; + + private final boolean useSsl; + + private final String username; + + private final String password; + + ConnectionInfo(URI uri, boolean useSsl, String username, String password) { + this.uri = uri; + this.useSsl = useSsl; + this.username = username; + this.password = password; + } + + boolean isUseSsl() { + return this.useSsl; + } + + String getHostName() { + return this.uri.getHost(); + } + + int getPort() { + return this.uri.getPort(); + } + + String getUsername() { + return this.username; + } + + String getPassword() { + return this.password; + } + + } + +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java new file mode 100644 index 000000000000..837f5106bea7 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; + +import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; +import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; +import com.azure.identity.extensions.implementation.token.AccessTokenResolver; + +import java.util.Properties; +import java.util.function.Supplier; + +public class AzureRedisCredentialSupplier implements Supplier { + + private final AzureAuthenticationTemplate azureAuthenticationTemplate; + + public AzureRedisCredentialSupplier(Properties properties) { + this(properties, null, null); + } + + public AzureRedisCredentialSupplier(Properties properties, TokenCredentialProvider tokenCredentialProvider, AccessTokenResolver accessTokenResolver) { + azureAuthenticationTemplate = new AzureAuthenticationTemplate(tokenCredentialProvider, accessTokenResolver); + azureAuthenticationTemplate.init(properties); + } + + public AzureRedisCredentialSupplier(TokenCredentialProvider tokenCredentialProvider, AccessTokenResolver accessTokenResolver) { + azureAuthenticationTemplate = new AzureAuthenticationTemplate(tokenCredentialProvider, accessTokenResolver); + } + + @Override + public char[] get() { + return azureAuthenticationTemplate.getTokenAsPassword().toCharArray(); + } + +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java new file mode 100644 index 000000000000..eb97e8a7f782 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; + +class RedisUrlSyntaxException extends RuntimeException { + + private final String url; + + RedisUrlSyntaxException(String url, Exception cause) { + super(buildMessage(url), cause); + this.url = url; + } + + RedisUrlSyntaxException(String url) { + super(buildMessage(url)); + this.url = url; + } + + String getUrl() { + return this.url; + } + + private static String buildMessage(String url) { + return "Invalid Redis URL '" + url + "'"; + } + +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories index df099c095a1b..9ce2e51fb823 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories @@ -38,7 +38,8 @@ com.azure.spring.cloud.autoconfigure.resourcemanager.AzureEventHubsResourceManag com.azure.spring.cloud.autoconfigure.resourcemanager.AzureServiceBusResourceManagerAutoConfiguration,\ com.azure.spring.cloud.autoconfigure.resourcemanager.AzureStorageQueueResourceManagerAutoConfiguration,\ com.azure.spring.cloud.autoconfigure.compatibility.AzureCompatibilityVerifierAutoConfiguration,\ -com.azure.spring.cloud.autoconfigure.jdbc.AzureJdbcAutoConfiguration +com.azure.spring.cloud.autoconfigure.jdbc.AzureJdbcAutoConfiguration,\ +com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis.AzureJedisPasswordlessConnectionConfiguration org.springframework.boot.diagnostics.FailureAnalyzer=\ com.azure.spring.cloud.autoconfigure.implementation.compatibility.AzureCompatibilityNotMetFailureAnalyzer diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java new file mode 100644 index 000000000000..1a0313bbc50d --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -0,0 +1,227 @@ +package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.data.redis.JedisClientConfigurationBuilderCustomizer; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link AzureJedisPasswordlessConnectionConfiguration} when Lettuce is not on the classpath. + */ +class AzureJedisPasswordlessAutoConfigurationTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withPropertyValues( + "spring.redis.azure.passwordless-enabled = true", + "spring.redis.username = testuser", + "spring.redis.host = testhost" + ) + .withConfiguration(AutoConfigurations.of(AzureJedisPasswordlessConnectionConfiguration.class)) + ; + + @Test + void connectionFactoryDefaultsToJedis() { + this.contextRunner.run((context) -> assertThat(context.getBean("azureRedisConnectionFactory")) + .isInstanceOf(AzureJedisConnectionFactory.class)); + } + + + @Test + void testOverrideRedisConfiguration() { + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1").run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getDatabase()).isEqualTo(1); + assertThat(cf.getPassword()).isNull(); + assertThat(cf.isUseSsl()).isTrue(); + }); + } + + @Test + void testUseSsl() { + this.contextRunner.run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.isUseSsl()).isTrue(); + }); + } + + @Test + void testRedisUrlConfiguration() { + this.contextRunner + .withPropertyValues("spring.redis.host:foo", "spring.redis.url:redis://user:password@example:33") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("example"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.isUseSsl()).isTrue(); + }); + } + + @Test + void testOverrideUrlRedisConfiguration() { + this.contextRunner + .withPropertyValues("spring.redis.host:foo", "spring.redis.password:xyz", "spring.redis.port:1000", + "spring.redis.ssl:false", "spring.redis.url:rediss://user:password@example:33") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("example"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.isUseSsl()).isTrue(); + }); + } + + @Test + void testPasswordInUrlWithColon() { + this.contextRunner.withPropertyValues("spring.redis.url:redis://:pass:word@example:33").run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("example"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(cf.getPassword()).isEqualTo("pass:word"); + }); + } + + @Test + void testPasswordInUrlStartsWithColon() { + this.contextRunner.withPropertyValues("spring.redis.url:redis://user::pass:word@example:33").run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("example"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(cf.getPassword()).isEqualTo(":pass:word"); + }); + } + + @Test + void testRedisConfigurationWithPool() { + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.jedis.pool.min-idle:1", + "spring.redis.jedis.pool.max-idle:4", "spring.redis.jedis.pool.max-active:16", + "spring.redis.jedis.pool.max-wait:2000", "spring.redis.jedis.pool.time-between-eviction-runs:30000") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getPoolConfig().getMinIdle()).isEqualTo(1); + assertThat(cf.getPoolConfig().getMaxIdle()).isEqualTo(4); + assertThat(cf.getPoolConfig().getMaxTotal()).isEqualTo(16); + assertThat(cf.getPoolConfig().getMaxWaitDuration()).isEqualTo(Duration.ofSeconds(2)); + assertThat(cf.getPoolConfig().getDurationBetweenEvictionRuns()).isEqualTo(Duration.ofSeconds(30)); + }); + } + + @Test + void testRedisConfigurationDisabledPool() { + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.jedis.pool.enabled:false") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getClientConfiguration().isUsePooling()).isEqualTo(false); + }); + } + + @Test + void testRedisConfigurationWithTimeoutAndConnectTimeout() { + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.timeout:250", + "spring.redis.connect-timeout:1000").run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getTimeout()).isEqualTo(250); + assertThat(cf.getClientConfiguration().getConnectTimeout().toMillis()).isEqualTo(1000); + }); + } + + @Test + void testRedisConfigurationWithDefaultTimeouts() { + this.contextRunner.withPropertyValues("spring.redis.host:foo").run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getTimeout()).isEqualTo(2000); + assertThat(cf.getClientConfiguration().getConnectTimeout().toMillis()).isEqualTo(2000); + }); + } + + @Test + void testRedisConfigurationWithClientName() { + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.client-name:spring-boot") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getClientName()).isEqualTo("spring-boot"); + }); + } + + //TODO question will support sentinel? + // @Test + // void testRedisConfigurationWithSentinel() { + // this.contextRunner + // .withPropertyValues("spring.redis.sentinel.master:mymaster", + // "spring.redis.sentinel.nodes:127.0.0.1:26379,127.0.0.1:26380") + // .withUserConfiguration(JedisConnectionFactoryCaptorConfiguration.class) + // .run((context) -> assertThat(JedisConnectionFactoryCaptor.connectionFactory.isRedisSentinelAware()) + // .isTrue()); + // } + + // TODO will support Sentinel? + // @Test + // void testRedisConfigurationWithSentinelAndAuthentication() { + // this.contextRunner.withPropertyValues("spring.redis.username=user", "spring.redis.password=password", + // "spring.redis.sentinel.master:mymaster", "spring.redis.sentinel.nodes:127.0.0.1:26379,127.0.0.1:26380") + // .withUserConfiguration(JedisConnectionFactoryCaptorConfiguration.class).run((context) -> { + // assertThat(JedisConnectionFactoryCaptor.connectionFactory.isRedisSentinelAware()).isTrue(); + // assertThat(JedisConnectionFactoryCaptor.connectionFactory.getPassword()).isEqualTo("password"); + // }); + // } + + // TODO will support cluster? + // @Test + // void testRedisConfigurationWithCluster() { + // this.contextRunner.withPropertyValues("spring.redis.cluster.nodes=127.0.0.1:27379,127.0.0.1:27380") + // .withUserConfiguration(JedisConnectionFactoryCaptorConfiguration.class) + // .run((context) -> assertThat(JedisConnectionFactoryCaptor.connectionFactory.isRedisClusterAware()) + // .isTrue()); + // } + + + @Configuration(proxyBeanMethods = false) + static class CustomConfiguration { + + @Bean + JedisClientConfigurationBuilderCustomizer customizer() { + return JedisClientConfigurationBuilder::useSsl; + } + + } + + @Configuration(proxyBeanMethods = false) + static class JedisConnectionFactoryCaptorConfiguration { + + @Bean + JedisConnectionFactoryCaptor jedisConnectionFactoryCaptor() { + return new JedisConnectionFactoryCaptor(); + } + + } + + static class JedisConnectionFactoryCaptor implements BeanPostProcessor { + + static JedisConnectionFactory connectionFactory; + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) { + if (bean instanceof JedisConnectionFactory) { + connectionFactory = (JedisConnectionFactory) bean; + } + return bean; + } + + } + +} diff --git a/sdk/spring/spring-cloud-azure-service/pom.xml b/sdk/spring/spring-cloud-azure-service/pom.xml index 5c07be7c5821..716f9f7f7383 100644 --- a/sdk/spring/spring-cloud-azure-service/pom.xml +++ b/sdk/spring/spring-cloud-azure-service/pom.xml @@ -112,10 +112,18 @@ true + + + redis.clients + jedis + 3.8.0 + true + + org.mockito mockito-core - 4.5.1 + 4.5.1 test diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java index 2c1fc1f36793..630fac65daf8 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java @@ -3,6 +3,8 @@ package com.azure.spring.cloud.service.implementation.passwordless; +import com.azure.identity.extensions.implementation.enums.AuthProperty; +import com.azure.spring.cloud.core.implementation.properties.PropertyMapper; import com.azure.spring.cloud.core.properties.AzureProperties; import com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; import com.azure.spring.cloud.core.properties.client.ClientProperties; @@ -11,14 +13,21 @@ import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; import com.azure.spring.cloud.core.provider.authentication.TokenCredentialOptionsProvider; +import java.util.Properties; +import java.util.function.BiConsumer; +import java.util.function.Function; + /** * Implement {@link TokenCredentialOptionsProvider} and {@link AzureProfileOptionsProvider} for Spring Cloud Azure * support for other third party services. */ public class AzurePasswordlessProperties implements AzureProperties { + private static final PropertyMapper PROPERTY_MAPPER = new PropertyMapper(); private AzureProfileProperties profile = new AzureProfileProperties(); + private String scopes; + private TokenCredentialProperties credential = new TokenCredentialProperties(); // Use client options inside credential for azure identity @@ -73,4 +82,79 @@ public boolean isPasswordlessEnabled() { public void setPasswordlessEnabled(boolean passwordlessEnabled) { this.passwordlessEnabled = passwordlessEnabled; } + + public String getScopes() { + return scopes; + } + + public void setScopes(String scopes) { + this.scopes = scopes; + } + + public Properties toProperties() { + Properties target = new Properties(); + for (AzurePasswordlessPropertiesMapping m : AzurePasswordlessPropertiesMapping.values()) { + PROPERTY_MAPPER.from(m.getter().apply(this)).to(v -> m.setter.accept(target, v)); + } + return target; + } + + private enum AzurePasswordlessPropertiesMapping { + + scopes( + p -> p.getScopes(), + (p, s) -> p.setProperty(AuthProperty.SCOPES.getPropertyKey(), s)), + + clientCertificatePassword( + p -> p.getCredential().getClientCertificatePassword(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PASSWORD.getPropertyKey(), s)), + + clientCertificatePath( + p -> p.getCredential().getClientCertificatePath(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PATH.getPropertyKey(), s)), + + clientId( + p -> p.getCredential().getClientId(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_ID.getPropertyKey(), s)), + + clientSecret( + p -> p.getCredential().getClientSecret(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_SECRET.getPropertyKey(), s)), + + managedIdentityEnabled( + + p -> String.valueOf(p.getCredential().isManagedIdentityEnabled()), + (p, s) -> p.setProperty(AuthProperty.MANAGED_IDENTITY_ENABLED.getPropertyKey(), s)), + + password( + p -> p.getCredential().getPassword(), + (p, s) -> p.setProperty(AuthProperty.PASSWORD.getPropertyKey(), s)), + + username( + p -> p.getCredential().getUsername(), + (p, s) -> p.setProperty(AuthProperty.USERNAME.getPropertyKey(), s)), + + + tenantId( + p -> p.getProfile().getTenantId(), + (p, s) -> p.setProperty(AuthProperty.TENANT_ID.getPropertyKey(), s)); + + private Function getter; + private BiConsumer setter; + + AzurePasswordlessPropertiesMapping(Function getter, BiConsumer setter) { + this.getter = getter; + this.setter = setter; + } + + public Function getter() { + return getter; + } + + public BiConsumer setter() { + return setter; + } + + } } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java new file mode 100644 index 000000000000..d0e9630dc28e --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.service.implementation.passwordless; + +import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; +import java.util.HashMap; +import java.util.Map; + +/** + * + */ +public class AzureRedisPasswordlessProperties extends AzurePasswordlessProperties { + + // todo + private static final Map REDIS_SCOPE_MAP = new HashMap() { + { + put(AzureProfileOptionsProvider.CloudType.AZURE, "https://*.cacheinfra.windows.net:10225/appid/.default"); + put(AzureProfileOptionsProvider.CloudType.AZURE_CHINA, "https://*.cacheinfra.windows.net:10225/appid/.default"); + put(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY, "https://*.cacheinfra.windows.net:10225/appid/.default"); + put(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT, "https://*.cacheinfra.windows.net:10225/appid/.default"); + } + }; + + + @Override + public String getScopes() { + if (super.getScopes() == null) { + super.setScopes(getRedisScopes()); + } + return super.getScopes(); + } + + private String getRedisScopes() { + String redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE); + AzureProfileOptionsProvider.CloudType cloudType = getProfile().getCloudType(); + if (AzureProfileOptionsProvider.CloudType.AZURE.equals(cloudType)) { + redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE); + } else if (AzureProfileOptionsProvider.CloudType.AZURE_CHINA.equals(cloudType)) { + redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE_CHINA); + } else if (AzureProfileOptionsProvider.CloudType.AZURE_GERMANY.equals(cloudType)) { + redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY); + } else if (AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT.equals(cloudType)) { + redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY); + } + return redisScope; + } + +} diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisClientConfig.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisClientConfig.java new file mode 100644 index 000000000000..3dbd462853cf --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisClientConfig.java @@ -0,0 +1,221 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.service.redis; + +import redis.clients.jedis.HostAndPortMapper; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.Protocol; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocketFactory; +import java.util.Objects; +import java.util.function.Supplier; + +public final class AzureJedisClientConfig implements JedisClientConfig { + + private final int connectionTimeoutMillis; + private final int socketTimeoutMillis; + private final int blockingSocketTimeoutMillis; + + private final String user; + private volatile String password; + private final int database; + private final String clientName; + + private final boolean ssl; + private final SSLSocketFactory sslSocketFactory; + private final SSLParameters sslParameters; + private final HostnameVerifier hostnameVerifier; + + private final HostAndPortMapper hostAndPortMapper; + + private Supplier credentialSupplier; + + private AzureJedisClientConfig(int connectionTimeoutMillis, int soTimeoutMillis, + int blockingSocketTimeoutMillis, String user, String password, int database, String clientName, + boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, + HostnameVerifier hostnameVerifier, HostAndPortMapper hostAndPortMapper, Supplier credentialSupplier) { + this.connectionTimeoutMillis = connectionTimeoutMillis; + this.socketTimeoutMillis = soTimeoutMillis; + this.blockingSocketTimeoutMillis = blockingSocketTimeoutMillis; + this.user = user; + this.password = password; + this.database = database; + this.clientName = clientName; + this.ssl = ssl; + this.sslSocketFactory = sslSocketFactory; + this.sslParameters = sslParameters; + this.hostnameVerifier = hostnameVerifier; + this.hostAndPortMapper = hostAndPortMapper; + this.credentialSupplier = credentialSupplier; + } + + @Override + public int getConnectionTimeoutMillis() { + return connectionTimeoutMillis; + } + + @Override + public int getSocketTimeoutMillis() { + return socketTimeoutMillis; + } + + @Override + public int getBlockingSocketTimeoutMillis() { + return blockingSocketTimeoutMillis; + } + + @Override + public String getUser() { + return user; + } + + @Override + public String getPassword() { + return this.password != null ? this.password : new String(this.credentialSupplier.get()); + } + + @Override + public synchronized void updatePassword(String password) { + if (!Objects.equals(this.password, password)) { + this.password = password; + } + } + + @Override + public int getDatabase() { + return database; + } + + @Override + public String getClientName() { + return clientName; + } + + @Override + public boolean isSsl() { + return ssl; + } + + @Override + public SSLSocketFactory getSslSocketFactory() { + return sslSocketFactory; + } + + @Override + public SSLParameters getSslParameters() { + return sslParameters; + } + + @Override + public HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + + @Override + public HostAndPortMapper getHostAndPortMapper() { + return hostAndPortMapper; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private int connectionTimeoutMillis = Protocol.DEFAULT_TIMEOUT; + private int socketTimeoutMillis = Protocol.DEFAULT_TIMEOUT; + private int blockingSocketTimeoutMillis = 0; + + private String user = null; + private String password = null; + + private Supplier credentialSupplier = null; + private int database = Protocol.DEFAULT_DATABASE; + private String clientName = null; + + private boolean ssl = false; + private SSLSocketFactory sslSocketFactory = null; + private SSLParameters sslParameters = null; + private HostnameVerifier hostnameVerifier = null; + + private HostAndPortMapper hostAndPortMapper = null; + + private Builder() { + } + + public AzureJedisClientConfig build() { + return new AzureJedisClientConfig(connectionTimeoutMillis, socketTimeoutMillis, + blockingSocketTimeoutMillis, user, password, database, clientName, ssl, sslSocketFactory, + sslParameters, hostnameVerifier, hostAndPortMapper, credentialSupplier); + } + + public Builder connectionTimeoutMillis(int connectionTimeoutMillis) { + this.connectionTimeoutMillis = connectionTimeoutMillis; + return this; + } + + public Builder socketTimeoutMillis(int socketTimeoutMillis) { + this.socketTimeoutMillis = socketTimeoutMillis; + return this; + } + + public Builder blockingSocketTimeoutMillis(int blockingSocketTimeoutMillis) { + this.blockingSocketTimeoutMillis = blockingSocketTimeoutMillis; + return this; + } + + public Builder user(String user) { + this.user = user; + return this; + } + + public Builder password(String password) { + this.password = password; + return this; + } + + public Builder credentialSupplier(Supplier credentialSupplier) { + this.credentialSupplier = credentialSupplier; + return this; + } + + public Builder database(int database) { + this.database = database; + return this; + } + + public Builder clientName(String clientName) { + this.clientName = clientName; + return this; + } + + public Builder ssl(boolean ssl) { + this.ssl = ssl; + return this; + } + + public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) { + this.sslSocketFactory = sslSocketFactory; + return this; + } + + public Builder sslParameters(SSLParameters sslParameters) { + this.sslParameters = sslParameters; + return this; + } + + public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + return this; + } + + public Builder hostAndPortMapper(HostAndPortMapper hostAndPortMapper) { + this.hostAndPortMapper = hostAndPortMapper; + return this; + } + } + +} diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisFactory.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisFactory.java new file mode 100644 index 000000000000..b69b4e20e02d --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisFactory.java @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.service.redis; + +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.PooledObjectFactory; +import org.apache.commons.pool2.impl.DefaultPooledObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.DefaultJedisSocketFactory; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.JedisSocketFactory; +import redis.clients.jedis.exceptions.JedisException; + +/** + * PoolableObjectFactory custom impl for Azure Redis. + */ +public class AzureJedisFactory implements PooledObjectFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(AzureJedisFactory.class); + + private final JedisSocketFactory jedisSocketFactory; + + private final JedisClientConfig clientConfig; + + protected AzureJedisFactory(HostAndPort hostAndPort, JedisClientConfig clientConfig) { + this.clientConfig = clientConfig; + this.jedisSocketFactory = new DefaultJedisSocketFactory(hostAndPort, this.clientConfig); + } + + + @Override + public void activateObject(PooledObject pooledJedis) { + final Jedis jedis = pooledJedis.getObject(); + if (jedis.getDB() != clientConfig.getDatabase()) { + jedis.select(clientConfig.getDatabase()); + } + } + + @Override + public void destroyObject(PooledObject pooledJedis) { + final Jedis jedis = pooledJedis.getObject(); + if (jedis.isConnected()) { + try { + // need a proper test, probably with mock + if (!jedis.isBroken()) { + jedis.quit(); + } + } catch (RuntimeException e) { + LOGGER.warn("Error while QUIT", e); + } + try { + jedis.close(); + } catch (RuntimeException e) { + LOGGER.warn("Error while close", e); + } + } + } + + @Override + public PooledObject makeObject() { + Jedis jedis = null; + try { + jedis = new Jedis(jedisSocketFactory, clientConfig); + jedis.connect(); + return new DefaultPooledObject<>(jedis); + } catch (JedisException jedisException) { + if (jedis != null) { + try { + jedis.quit(); + } catch (RuntimeException e) { + LOGGER.warn("Error while QUIT", e); + } + try { + jedis.close(); + } catch (RuntimeException e) { + LOGGER.warn("Error while close", e); + } + } + throw jedisException; + } + } + + @Override + public void passivateObject(PooledObject pooledObject) throws Exception { + + } + + @Override + public boolean validateObject(PooledObject pooledJedis) { + final Jedis jedis = pooledJedis.getObject(); + try { + String host = jedisSocketFactory.getHost(); + int port = jedisSocketFactory.getPort(); + + String connectionHost = jedis.getClient().getHost(); + int connectionPort = jedis.getClient().getPort(); + + return host.equals(connectionHost) + && port == connectionPort && jedis.isConnected() + && jedis.ping().equals("PONG"); + } catch (final Exception e) { + LOGGER.error("Error while validating pooled Jedis object.", e); + return false; + } + } + +} diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisPool.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisPool.java new file mode 100644 index 000000000000..e87fe142069b --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisPool.java @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.service.redis; + +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.JedisPool; + +public class AzureJedisPool extends JedisPool { + + public AzureJedisPool(final GenericObjectPoolConfig poolConfig, final HostAndPort hostAndPort, + final JedisClientConfig clientConfig) { + super(poolConfig, new AzureJedisFactory(hostAndPort, clientConfig)); + } +} diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/package-info.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/package-info.java new file mode 100644 index 000000000000..6d9d5cc2a059 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/package-info.java @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.service.redis; diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/redis/AzureJedisPoolTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/redis/AzureJedisPoolTest.java new file mode 100644 index 000000000000..c06952315fc2 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/redis/AzureJedisPoolTest.java @@ -0,0 +1,533 @@ +//package com.azure.spring.cloud.service.redis; +// +//import org.apache.commons.pool2.PooledObject; +//import org.apache.commons.pool2.PooledObjectFactory; +//import org.apache.commons.pool2.impl.DefaultPooledObject; +//import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +//import org.junit.jupiter.api.Assertions; +//import org.junit.jupiter.api.Test; +//import redis.clients.jedis.DefaultJedisClientConfig; +//import redis.clients.jedis.HostAndPort; +//import redis.clients.jedis.Jedis; +//import redis.clients.jedis.JedisFactory; +//import redis.clients.jedis.JedisPool; +//import redis.clients.jedis.JedisPoolConfig; +//import redis.clients.jedis.Protocol; +//import redis.clients.jedis.Transaction; +//import redis.clients.jedis.exceptions.InvalidURIException; +//import redis.clients.jedis.exceptions.JedisConnectionException; +//import redis.clients.jedis.exceptions.JedisException; +// +//import java.net.URI; +//import java.net.URISyntaxException; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.concurrent.atomic.AtomicInteger; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static org.junit.jupiter.api.Assertions.assertNull; +//import static org.junit.jupiter.api.Assertions.assertSame; +//import static org.junit.jupiter.api.Assertions.assertTrue; +//import static org.junit.jupiter.api.Assertions.fail; +// +//public class AzureJedisPoolTest { +// +// private static final HostAndPort hnp = HostAndPorts.getRedisServers().get(0); +// +// @Test +// public void checkConnections() { +// JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000); +// try (Jedis jedis = pool.getResource()) { +// jedis.auth("foobared"); +// jedis.set("foo", "bar"); +// assertEquals("bar", jedis.get("foo")); +// } +// pool.close(); +// assertTrue(pool.isClosed()); +// } +// +// @Test +// public void checkResourceWithConfig() { +// try (JedisPool pool = new JedisPool(HostAndPorts.getRedisServers().get(7), +// DefaultJedisClientConfig.builder().socketTimeoutMillis(5000).build())) { +// +// try (Jedis jedis = pool.getResource()) { +// assertEquals("PONG", jedis.ping()); +// assertEquals(5000, jedis.getClient().getSoTimeout()); +// jedis.close(); +// } +// } +// } +// +// @Test +// public void checkCloseableConnections() throws Exception { +// JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000); +// try (Jedis jedis = pool.getResource()) { +// jedis.auth("foobared"); +// jedis.set("foo", "bar"); +// assertEquals("bar", jedis.get("foo")); +// } +// pool.close(); +// assertTrue(pool.isClosed()); +// } +// +// @Test +// public void checkConnectionWithDefaultHostAndPort() { +// JedisPool pool = new JedisPool(new JedisPoolConfig()); +// try (Jedis jedis = pool.getResource()) { +// jedis.auth("foobared"); +// jedis.set("foo", "bar"); +// assertEquals("bar", jedis.get("foo")); +// } +// pool.close(); +// assertTrue(pool.isClosed()); +// } +// +// @Test +// public void checkResourceIsClosableAndReusable() { +// GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); +// config.setMaxTotal(1); +// config.setBlockWhenExhausted(false); +// try (JedisPool pool = new JedisPool(config, hnp.getHost(), hnp.getPort(), 2000, "foobared", 0, +// "closable-resuable-pool", false, null, null, null)) { +// +// Jedis jedis = pool.getResource(); +// jedis.set("hello", "jedis"); +// jedis.close(); +// +// Jedis jedis2 = pool.getResource(); +// assertEquals(jedis, jedis2); +// assertEquals("jedis", jedis2.get("hello")); +// jedis2.close(); +// } +// } +// +// @Test +// public void checkPoolRepairedWhenJedisIsBroken() { +// JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort()); +// try (Jedis jedis = pool.getResource()) { +// jedis.auth("foobared"); +// jedis.set("foo", "0"); +// jedis.quit(); +// } +// +// try (Jedis jedis = pool.getResource()) { +// jedis.auth("foobared"); +// jedis.incr("foo"); +// } +// pool.close(); +// assertTrue(pool.isClosed()); +// } +// +// @Test +// public void checkPoolOverflow() { +// Assertions.assertThrows(JedisException.class, () -> { +// GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); +// config.setMaxTotal(1); +// config.setBlockWhenExhausted(false); +// try (JedisPool pool = new JedisPool(config, hnp.getHost(), hnp.getPort()); +// Jedis jedis = pool.getResource()) { +// jedis.auth("foobared"); +// +// try (Jedis jedis2 = pool.getResource()) { +// jedis2.auth("foobared"); +// } +// } +// }); +// } +// +// @Test +// public void securePool() { +// JedisPoolConfig config = new JedisPoolConfig(); +// config.setTestOnBorrow(true); +// JedisPool pool = new JedisPool(config, hnp.getHost(), hnp.getPort(), 2000, "foobared"); +// try (Jedis jedis = pool.getResource()) { +// jedis.set("foo", "bar"); +// } +// pool.close(); +// assertTrue(pool.isClosed()); +// } +// +// @Test +// public void nonDefaultDatabase() { +// try (JedisPool pool0 = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, +// "foobared"); Jedis jedis0 = pool0.getResource()) { +// jedis0.set("foo", "bar"); +// assertEquals("bar", jedis0.get("foo")); +// } +// +// try (JedisPool pool1 = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, +// "foobared", 1); Jedis jedis1 = pool1.getResource()) { +// assertNull(jedis1.get("foo")); +// } +// } +// +// @Test +// public void startWithUrlString() { +// try (Jedis j = new Jedis("localhost", 6380)) { +// j.auth("foobared"); +// j.select(2); +// j.set("foo", "bar"); +// } +// +// try (JedisPool pool = new JedisPool("redis://:foobared@localhost:6380/2"); +// Jedis jedis = pool.getResource()) { +// assertEquals("PONG", jedis.ping()); +// assertEquals("bar", jedis.get("foo")); +// } +// } +// +// @Test +// public void startWithUrl() throws URISyntaxException { +// try (Jedis j = new Jedis("localhost", 6380)) { +// j.auth("foobared"); +// j.select(2); +// j.set("foo", "bar"); +// } +// +// try (JedisPool pool = new JedisPool(new URI("redis://:foobared@localhost:6380/2")); +// Jedis jedis = pool.getResource()) { +// assertEquals("bar", jedis.get("foo")); +// } +// } +// +// @Test +// public void shouldThrowInvalidURIExceptionForInvalidURI() throws URISyntaxException { +// Assertions.assertThrows(InvalidURIException.class, () -> { +// new JedisPool(new URI("localhost:6380")).close(); +// }); +// } +// +// @Test +// public void allowUrlWithNoDBAndNoPassword() throws URISyntaxException { +// new JedisPool("redis://localhost:6380").close(); +// new JedisPool(new URI("redis://localhost:6380")).close(); +// } +// +// @Test +// public void selectDatabaseOnActivation() { +// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, +// "foobared")) { +// +// Jedis jedis0 = pool.getResource(); +// assertEquals(0, jedis0.getDB()); +// +// jedis0.select(1); +// assertEquals(1, jedis0.getDB()); +// +// jedis0.close(); +// +// Jedis jedis1 = pool.getResource(); +// assertTrue(jedis1 == jedis0, "Jedis instance was not reused"); +// assertEquals(0, jedis1.getDB()); +// +// jedis1.close(); +// } +// } +// +// @Test +// public void customClientName() { +// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, +// "foobared", 0, "my_shiny_client_name"); Jedis jedis = pool.getResource()) { +// +// assertEquals("my_shiny_client_name", jedis.clientGetname()); +// } +// } +// +// @Test +// public void returnResourceDestroysResourceOnException() { +// +// class CrashingJedis extends Jedis { +// @Override +// public void resetState() { +// throw new RuntimeException(); +// } +// } +// +// final AtomicInteger destroyed = new AtomicInteger(0); +// +// class CrashingJedisPooledObjectFactory implements PooledObjectFactory { +// +// @Override +// public PooledObject makeObject() throws Exception { +// return new DefaultPooledObject(new CrashingJedis()); +// } +// +// @Override +// public void destroyObject(PooledObject p) throws Exception { +// destroyed.incrementAndGet(); +// } +// +// @Override +// public boolean validateObject(PooledObject p) { +// return true; +// } +// +// @Override +// public void activateObject(PooledObject p) throws Exception { +// } +// +// @Override +// public void passivateObject(PooledObject p) throws Exception { +// } +// } +// +// GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); +// config.setMaxTotal(1); +// JedisPool pool = new JedisPool(config, new CrashingJedisPooledObjectFactory()); +// Jedis crashingJedis = pool.getResource(); +// +// try { +// crashingJedis.close(); +// } catch (Exception ignored) { +// } +// +// assertEquals(1, destroyed.get()); +// } +// +// @Test +// public void returnResourceShouldResetState() { +// GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); +// config.setMaxTotal(1); +// config.setBlockWhenExhausted(false); +// JedisPool pool = new JedisPool(config, hnp.getHost(), hnp.getPort(), 2000, "foobared"); +// +// Jedis jedis = pool.getResource(); +// try { +// jedis.set("hello", "jedis"); +// Transaction t = jedis.multi(); +// t.set("hello", "world"); +// } finally { +// jedis.close(); +// } +// +// Jedis jedis2 = pool.getResource(); +// try { +// assertTrue(jedis == jedis2); +// assertEquals("jedis", jedis2.get("hello")); +// } finally { +// jedis2.close(); +// } +// +// pool.close(); +// assertTrue(pool.isClosed()); +// } +// +// @Test +// public void getNumActiveWhenPoolIsClosed() { +// JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, +// "foobared", 0, "my_shiny_client_name"); +// +// try (Jedis j = pool.getResource()) { +// j.ping(); +// } +// +// pool.close(); +// assertEquals(0, pool.getNumActive()); +// } +// +// @Test +// public void getNumActiveReturnsTheCorrectNumber() { +// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000)) { +// Jedis jedis = pool.getResource(); +// jedis.auth("foobared"); +// jedis.set("foo", "bar"); +// assertEquals("bar", jedis.get("foo")); +// +// assertEquals(1, pool.getNumActive()); +// +// Jedis jedis2 = pool.getResource(); +// jedis.auth("foobared"); +// jedis.set("foo", "bar"); +// +// assertEquals(2, pool.getNumActive()); +// +// jedis.close(); +// assertEquals(1, pool.getNumActive()); +// +// jedis2.close(); +// +// assertEquals(0, pool.getNumActive()); +// } +// } +// +// @Test +// public void testAddObject() { +// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000)) { +// pool.addObjects(1); +// assertEquals(1, pool.getNumIdle()); +// } +// } +// +// @Test +// public void closeResourceTwice() { +// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000)) { +// Jedis j = pool.getResource(); +// j.auth("foobared"); +// j.ping(); +// j.close(); +// j.close(); +// } +// } +// +// @Test +// public void closeBrokenResourceTwice() { +// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000)) { +// Jedis j = pool.getResource(); +// try { +// // make connection broken +// j.getClient().getOne(); +// fail(); +// } catch (Exception e) { +// assertTrue(e instanceof JedisConnectionException); +// } +// assertTrue(j.isBroken()); +// j.close(); +// j.close(); +// } +// } +// +// @Test +// public void testCloseConnectionOnMakeObject() { +// JedisPoolConfig config = new JedisPoolConfig(); +// config.setTestOnBorrow(true); +// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, +// "wrong pass"); Jedis jedis = new Jedis("redis://:foobared@localhost:6379/")) { +// int currentClientCount = getClientCount(jedis.clientList()); +// try { +// pool.getResource(); +// fail("Should throw exception as password is incorrect."); +// } catch (Exception e) { +// assertEquals(currentClientCount, getClientCount(jedis.clientList())); +// } +// } +// } +// +// private int getClientCount(final String clientList) { +// return clientList.split("\n").length; +// } +// +// @Test +// public void testResetInvalidPassword() { +// JedisFactory factory = new JedisFactory(hnp.getHost(), hnp.getPort(), 2000, 2000, +// "foobared", 0, "my_shiny_client_name") { +// }; +// +// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), factory)) { +// Jedis obj1_ref; +// try (Jedis obj1_1 = pool.getResource()) { +// obj1_ref = obj1_1; +// obj1_1.set("foo", "bar"); +// assertEquals("bar", obj1_1.get("foo")); +// assertEquals(1, pool.getNumActive()); +// } +// assertEquals(0, pool.getNumActive()); +// try (Jedis obj1_2 = pool.getResource()) { +// assertSame(obj1_ref, obj1_2); +// assertEquals(1, pool.getNumActive()); +// factory.setPassword("wrong password"); +// try (Jedis obj2 = pool.getResource()) { +// fail("Should not get resource from pool"); +// } catch (JedisException e) { +// } +// assertEquals(1, pool.getNumActive()); +// } +// assertEquals(0, pool.getNumActive()); +// } +// } +// +// @Test +// public void testResetValidPassword() { +// JedisFactory factory = new JedisFactory(hnp.getHost(), hnp.getPort(), 2000, 2000, +// "bad password", 0, "my_shiny_client_name") { +// }; +// +// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), factory)) { +// try (Jedis obj1 = pool.getResource()) { +// fail("Should not get resource from pool"); +// } catch (JedisException e) { +// } +// assertEquals(0, pool.getNumActive()); +// +// factory.setPassword("foobared"); +// try (Jedis obj2 = pool.getResource()) { +// obj2.set("foo", "bar"); +// assertEquals("bar", obj2.get("foo")); +// } +// } +// } +// +// private final class HostAndPorts { +// +// private static List redisHostAndPortList = new ArrayList<>(); +// private static List sentinelHostAndPortList = new ArrayList<>(); +// private static List clusterHostAndPortList = new ArrayList<>(); +// +// static { +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT)); +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 1)); +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 2)); +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 3)); +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 4)); +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 5)); +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 6)); +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 7)); +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 8)); +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 9)); +// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 10)); +// +// sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT)); +// sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 1)); +// sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 2)); +// sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 3)); +// sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 4)); +// +// clusterHostAndPortList.add(new HostAndPort("localhost", 7379)); +// clusterHostAndPortList.add(new HostAndPort("localhost", 7380)); +// clusterHostAndPortList.add(new HostAndPort("localhost", 7381)); +// clusterHostAndPortList.add(new HostAndPort("localhost", 7382)); +// clusterHostAndPortList.add(new HostAndPort("localhost", 7383)); +// clusterHostAndPortList.add(new HostAndPort("localhost", 7384)); +// } +// +// public static List parseHosts(String envHosts, +// List existingHostsAndPorts) { +// +// if (null != envHosts && 0 < envHosts.length()) { +// +// String[] hostDefs = envHosts.split(","); +// +// if (null != hostDefs && 2 <= hostDefs.length) { +// +// List envHostsAndPorts = new ArrayList<>(hostDefs.length); +// +// for (String hostDef : hostDefs) { +// +// envHostsAndPorts.add(HostAndPort.from(hostDef)); +// } +// +// return envHostsAndPorts; +// } +// } +// +// return existingHostsAndPorts; +// } +// +// public static List getRedisServers() { +// return redisHostAndPortList; +// } +// +// public static List getSentinelServers() { +// return sentinelHostAndPortList; +// } +// +// public static List getClusterServers() { +// return clusterHostAndPortList; +// } +// +// private HostAndPorts() { +// throw new InstantiationError("Must not instantiate this class"); +// } +// } +// +//} diff --git a/sdk/spring/spring-cloud-azure-starter-redis/pom.xml b/sdk/spring/spring-cloud-azure-starter-redis/pom.xml new file mode 100644 index 000000000000..db219e1240f6 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-starter-redis/pom.xml @@ -0,0 +1,199 @@ + + + 4.0.0 + + com.azure.spring + spring-cloud-azure-starter-redis + 4.6.0-beta.1 + + Spring Cloud Azure Starter Redis + Spring Cloud Azure Starter for building applications with Azure Cache for Redis. Support authenticating with Azure AD. + https://microsoft.github.io/spring-cloud-azure + + + Spring Cloud Azure + SpringIntegSupport@microsoft.com + + + + scm:git:git@github.com:Azure/azure-sdk-for-java.git + scm:git:ssh://git@github.com:Azure/azure-sdk-for-java.git + https://github.com/Azure/azure-sdk-for-java + + + GitHub + https://github.com/Azure/azure-sdk-for-java/issues + + + + 1.8 + 1.8 + + + https://azuresdkartifacts.blob.core.windows.net/azure-sdk-for-java + + + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + repo + + + + + + + ossrh + Sonatype Snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + default + + true + daily + + + + + + + ossrh + Sonatype Snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + default + + true + always + + + + + + + ossrh + Sonatype Snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + true + default + + + azure-java-build-docs + ${site.url}/site/ + + + + + + com.azure.spring + spring-cloud-azure-starter + 4.6.0-beta.1 + + + + org.springframework.data + spring-data-redis + 2.7.6 + + + + redis.clients + jedis + 3.8.0 + + + + com.azure + azure-identity-extensions + 1.1.0-beta.1 + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.2 + + + + com.azure.spring.cloud.starter.redis + + + true + + + + + + + + empty-javadoc-jar-with-readme + package + + jar + + + javadoc + ${project.basedir}/javadocTemp + + + + empty-source-jar-with-readme + package + + jar + + + sources + ${project.basedir}/sourceTemp + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + copy-readme-to-javadocTemp-and-sourceTemp + prepare-package + + + Deleting existing ${project.basedir}/javadocTemp and + ${project.basedir}/sourceTemp + + + + + Copying ${project.basedir}/../README.md to + ${project.basedir}/javadocTemp/README.md + + + Copying ${project.basedir}/../README.md to + ${project.basedir}/sourceTemp/README.md + + + + + + run + + + + + + + + From 28693397caa8ba40445c8d9ac5cecea5afe3eb3c Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Thu, 5 Jan 2023 18:38:05 +0800 Subject: [PATCH 02/38] fix checkstyle --- .../checkstyle/checkstyle-suppressions.xml | 4 + .../jedis/AzureJedisConnectionFactory.java | 59 +- ...isPasswordlessConnectionConfiguration.java | 13 +- .../jedis/AzureRedisCredentialSupplier.java | 23 + .../jedis/RedisUrlSyntaxException.java | 30 +- .../passwordless/jedis/package-info.java | 7 + ...edisPasswordlessAutoConfigurationTest.java | 354 ++++++------ .../AzurePasswordlessProperties.java | 29 +- .../redis/AzureJedisClientConfig.java | 7 +- .../redis/AzureJedisFactory.java | 2 +- .../redis/AzureJedisPool.java | 2 +- .../cloud/service/redis/package-info.java | 4 - .../service/redis/AzureJedisPoolTest.java | 533 ------------------ 13 files changed, 288 insertions(+), 779 deletions(-) create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/package-info.java rename sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/{ => implementation}/redis/AzureJedisClientConfig.java (97%) rename sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/{ => implementation}/redis/AzureJedisFactory.java (98%) rename sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/{ => implementation}/redis/AzureJedisPool.java (91%) delete mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/package-info.java delete mode 100644 sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/redis/AzureJedisPoolTest.java diff --git a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml index 4588884ec682..658470eb9be5 100755 --- a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml +++ b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml @@ -433,6 +433,10 @@ the main ServiceBusClientBuilder. --> + + + + diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java index 5c797a044a0a..ecbe6b910beb 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -3,8 +3,8 @@ package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; -import com.azure.spring.cloud.service.redis.AzureJedisClientConfig; -import com.azure.spring.cloud.service.redis.AzureJedisPool; +import com.azure.spring.cloud.service.implementation.redis.AzureJedisClientConfig; +import com.azure.spring.cloud.service.implementation.redis.AzureJedisPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,11 +31,16 @@ import java.util.function.Supplier; +/** + * Connection factory creating Jedis based connections to Azure Redis. + * + * @since 4.6.0 + */ public class AzureJedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory { private static final Logger LOGGER = LoggerFactory.getLogger(AzureJedisConnectionFactory.class); - private RedisStandaloneConfiguration standaloneConfig; + private final RedisStandaloneConfiguration standaloneConfig; private final Supplier credentialSupplier; private final JedisClientConfiguration clientConfiguration; @@ -47,6 +52,13 @@ public class AzureJedisConnectionFactory implements InitializingBean, Disposable private boolean convertPipelineAndTxResults = true; + /** + * Constructs a new AzureJedisConnectionFactory instance using the given RedisStandaloneConfiguration, JedisClientConfiguration and CredentialSupplier. + * + * @param standaloneConfig must not be {@literal null}. + * @param clientConfiguration must not be {@literal null}. + * @param credentialSupplier must not be {@literal null}. + */ public AzureJedisConnectionFactory(RedisStandaloneConfiguration standaloneConfig, JedisClientConfiguration clientConfiguration, Supplier credentialSupplier) { this.standaloneConfig = standaloneConfig; this.clientConfiguration = clientConfiguration; @@ -123,18 +135,37 @@ public void setConvertPipelineAndTxResults(boolean convertPipelineAndTxResults) this.convertPipelineAndTxResults = convertPipelineAndTxResults; } + /** + * Indicates the use of a connection pool. + *

+ * Applies only to single node Redis. + * @return the use of connection pooling. + */ public boolean getUsePool() { return clientConfiguration.isUsePooling(); } + /** + * Returns the index of the database. + * + * @return the database index. + */ public int getDatabase() { return RedisConfiguration.getDatabaseOrElse(standaloneConfig, standaloneConfig::getDatabase); } + /** + * Returns the port used to connect to the Redis instance. + * + * @return the Redis port. + */ public int getPort() { return standaloneConfig.getPort(); } + /** + * @return {@literal true} to use SSL, {@literal false} to use unencrypted connections. + */ public boolean isUseSsl() { return clientConfiguration.isUseSsl(); } @@ -158,17 +189,29 @@ public String getPassword() { return getRedisPassword().map(String::new).orElse(null); } - // TODO how to not use @SuppressWarnings + /** + * Returns the poolConfig. + * + * @return the poolConfig + */ @SuppressWarnings("unchecked") @Nullable public GenericObjectPoolConfig getPoolConfig() { return clientConfiguration.getPoolConfig().orElse(null); } + /** + * @return the {@link JedisClientConfiguration}. + */ public JedisClientConfiguration getClientConfiguration() { return clientConfiguration; } + /** + * Returns the client name. + * + * @return the client name. + */ @Nullable public String getClientName() { return clientConfiguration.getClientName().orElse(null); @@ -194,6 +237,14 @@ protected Pool createRedisPool() { this.jedisClientConfig); } + + /** + * Returns a Jedis instance to be used as a Redis connection. The instance can be newly created or retrieved from a + * pool. + * + * @throws RedisConnectionFailureException when can't fetch a jedis instance. + * @return Jedis instance ready for wrapping into a {@link RedisConnection}. + */ protected Jedis fetchJedisConnector() { try { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java index 12c8948e099c..bf3b9fedebca 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java @@ -45,7 +45,7 @@ @ConditionalOnClass({GenericObjectPool.class, JedisConnection.class, Jedis.class}) @ConditionalOnExpression("${spring.redis.azure.passwordless-enabled:false}") @ConditionalOnMissingBean(RedisConnectionFactory.class) -@ConditionalOnProperty(prefix = "spring.redis", name = { "host", "username" }) +@ConditionalOnProperty(prefix = "spring.redis", name = {"host", "username"}) @EnableConfigurationProperties(RedisProperties.class) class AzureJedisPasswordlessConnectionConfiguration { @@ -74,7 +74,7 @@ AzureJedisConnectionFactory azureRedisConnectionFactory(RedisProperties redisPro RedisStandaloneConfiguration standaloneConfig = getStandaloneConfig(redisProperties); JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(redisProperties); - return new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration , azureRedisCredentialSupplier); + return new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, azureRedisCredentialSupplier); } @@ -157,8 +157,7 @@ private RedisStandaloneConfiguration getStandaloneConfig(RedisProperties redisPr config.setPort(connectionInfo.getPort()); config.setUsername(connectionInfo.getUsername()); config.setPassword(RedisPassword.of(connectionInfo.getPassword())); - } - else { + } else { config.setHostName(redisProperties.getHost()); config.setPort(redisProperties.getPort()); config.setUsername(redisProperties.getUsername()); @@ -187,14 +186,12 @@ ConnectionInfo parseUrl(String url) { if (index >= 0) { username = candidate.substring(0, index); password = candidate.substring(index + 1); - } - else { + } else { password = candidate; } } return new ConnectionInfo(uri, useSsl, username, password); - } - catch (URISyntaxException ex) { + } catch (URISyntaxException ex) { throw new RedisUrlSyntaxException(url, ex); } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java index 837f5106bea7..4dc481f35daf 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java @@ -3,6 +3,7 @@ package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +import com.azure.core.credential.TokenCredential; import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; import com.azure.identity.extensions.implementation.token.AccessTokenResolver; @@ -10,19 +11,41 @@ import java.util.Properties; import java.util.function.Supplier; +/** + * AzureRedisCredentialSupplier that provide a char array as the password to connect Azure Redis. + * + * @since 4.6.0 + */ public class AzureRedisCredentialSupplier implements Supplier { private final AzureAuthenticationTemplate azureAuthenticationTemplate; + /** + * Create {@link AzureRedisCredentialSupplier} instance. + * @param properties properties to initialize AzureRedisCredentialSupplier. + */ public AzureRedisCredentialSupplier(Properties properties) { this(properties, null, null); } + /** + * Create {@link AzureRedisCredentialSupplier} instance. + * @param properties properties to initialize AzureRedisCredentialSupplier. + * @param tokenCredentialProvider Supplier that provide a {@link TokenCredential}. + * @param accessTokenResolver An {@link AccessTokenResolver} instance, which will take a TokenCredential as input + * and outputs a publisher that emits a single access token. + */ public AzureRedisCredentialSupplier(Properties properties, TokenCredentialProvider tokenCredentialProvider, AccessTokenResolver accessTokenResolver) { azureAuthenticationTemplate = new AzureAuthenticationTemplate(tokenCredentialProvider, accessTokenResolver); azureAuthenticationTemplate.init(properties); } + /** + * Create {@link AzureRedisCredentialSupplier} instance. + * @param tokenCredentialProvider Supplier that provide a {@link TokenCredential}. + * @param accessTokenResolver An {@link AccessTokenResolver} instance, which will take a TokenCredential as input + * and outputs a publisher that emits a single access token. + */ public AzureRedisCredentialSupplier(TokenCredentialProvider tokenCredentialProvider, AccessTokenResolver accessTokenResolver) { azureAuthenticationTemplate = new AzureAuthenticationTemplate(tokenCredentialProvider, accessTokenResolver); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java index eb97e8a7f782..53b1c4fd7ff0 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java @@ -5,24 +5,24 @@ class RedisUrlSyntaxException extends RuntimeException { - private final String url; + private final String url; - RedisUrlSyntaxException(String url, Exception cause) { - super(buildMessage(url), cause); - this.url = url; - } + RedisUrlSyntaxException(String url, Exception cause) { + super(buildMessage(url), cause); + this.url = url; + } - RedisUrlSyntaxException(String url) { - super(buildMessage(url)); - this.url = url; - } + RedisUrlSyntaxException(String url) { + super(buildMessage(url)); + this.url = url; + } - String getUrl() { - return this.url; - } + String getUrl() { + return this.url; + } - private static String buildMessage(String url) { - return "Invalid Redis URL '" + url + "'"; - } + private static String buildMessage(String url) { + return "Invalid Redis URL '" + url + "'"; + } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/package-info.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/package-info.java new file mode 100644 index 000000000000..b977b05d833f --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/package-info.java @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/** + * Spring Cloud Azure's auto-configuration for Azure Redis passwordless connections. + */ +package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java index 1a0313bbc50d..3648b746d0d2 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; import org.junit.jupiter.api.Test; @@ -19,209 +22,178 @@ */ class AzureJedisPasswordlessAutoConfigurationTest { - private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withPropertyValues( - "spring.redis.azure.passwordless-enabled = true", - "spring.redis.username = testuser", - "spring.redis.host = testhost" - ) - .withConfiguration(AutoConfigurations.of(AzureJedisPasswordlessConnectionConfiguration.class)) - ; - - @Test - void connectionFactoryDefaultsToJedis() { - this.contextRunner.run((context) -> assertThat(context.getBean("azureRedisConnectionFactory")) - .isInstanceOf(AzureJedisConnectionFactory.class)); - } - - - @Test - void testOverrideRedisConfiguration() { - this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1").run((context) -> { - AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getDatabase()).isEqualTo(1); - assertThat(cf.getPassword()).isNull(); - assertThat(cf.isUseSsl()).isTrue(); - }); - } - - @Test - void testUseSsl() { - this.contextRunner.run((context) -> { - AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.isUseSsl()).isTrue(); - }); - } - - @Test - void testRedisUrlConfiguration() { - this.contextRunner - .withPropertyValues("spring.redis.host:foo", "spring.redis.url:redis://user:password@example:33") - .run((context) -> { - AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.isUseSsl()).isTrue(); - }); - } - - @Test - void testOverrideUrlRedisConfiguration() { - this.contextRunner - .withPropertyValues("spring.redis.host:foo", "spring.redis.password:xyz", "spring.redis.port:1000", - "spring.redis.ssl:false", "spring.redis.url:rediss://user:password@example:33") - .run((context) -> { - AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.isUseSsl()).isTrue(); - }); - } - - @Test - void testPasswordInUrlWithColon() { - this.contextRunner.withPropertyValues("spring.redis.url:redis://:pass:word@example:33").run((context) -> { + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withPropertyValues( + "spring.redis.azure.passwordless-enabled = true", + "spring.redis.username = testuser", + "spring.redis.host = testhost" + ) + .withConfiguration(AutoConfigurations.of(AzureJedisPasswordlessConnectionConfiguration.class)); + + @Test + void connectionFactoryDefaultsToJedis() { + this.contextRunner.run((context) -> assertThat(context.getBean("azureRedisConnectionFactory")) + .isInstanceOf(AzureJedisConnectionFactory.class)); + } + + + @Test + void testOverrideRedisConfiguration() { + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1").run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(cf.getPassword()).isEqualTo("pass:word"); - }); - } - - @Test - void testPasswordInUrlStartsWithColon() { - this.contextRunner.withPropertyValues("spring.redis.url:redis://user::pass:word@example:33").run((context) -> { + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getDatabase()).isEqualTo(1); + assertThat(cf.getPassword()).isNull(); + assertThat(cf.isUseSsl()).isTrue(); + }); + } + + @Test + void testUseSsl() { + this.contextRunner.run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(cf.getPassword()).isEqualTo(":pass:word"); - }); - } - - @Test - void testRedisConfigurationWithPool() { - this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.jedis.pool.min-idle:1", - "spring.redis.jedis.pool.max-idle:4", "spring.redis.jedis.pool.max-active:16", - "spring.redis.jedis.pool.max-wait:2000", "spring.redis.jedis.pool.time-between-eviction-runs:30000") - .run((context) -> { - AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getPoolConfig().getMinIdle()).isEqualTo(1); - assertThat(cf.getPoolConfig().getMaxIdle()).isEqualTo(4); - assertThat(cf.getPoolConfig().getMaxTotal()).isEqualTo(16); - assertThat(cf.getPoolConfig().getMaxWaitDuration()).isEqualTo(Duration.ofSeconds(2)); - assertThat(cf.getPoolConfig().getDurationBetweenEvictionRuns()).isEqualTo(Duration.ofSeconds(30)); - }); - } - - @Test - void testRedisConfigurationDisabledPool() { - this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.jedis.pool.enabled:false") - .run((context) -> { - AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getClientConfiguration().isUsePooling()).isEqualTo(false); - }); - } - - @Test - void testRedisConfigurationWithTimeoutAndConnectTimeout() { - this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.timeout:250", - "spring.redis.connect-timeout:1000").run((context) -> { + assertThat(cf.isUseSsl()).isTrue(); + }); + } + + @Test + void testRedisUrlConfiguration() { + this.contextRunner + .withPropertyValues("spring.redis.host:foo", "spring.redis.url:redis://user:password@example:33") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("example"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.isUseSsl()).isTrue(); + }); + } + + @Test + void testOverrideUrlRedisConfiguration() { + this.contextRunner + .withPropertyValues("spring.redis.host:foo", "spring.redis.password:xyz", "spring.redis.port:1000", + "spring.redis.ssl:false", "spring.redis.url:rediss://user:password@example:33") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("example"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(cf.getPassword()).isEqualTo("password"); + assertThat(cf.isUseSsl()).isTrue(); + }); + } + + @Test + void testPasswordInUrlWithColon() { + this.contextRunner.withPropertyValues("spring.redis.url:redis://:pass:word@example:33").run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getTimeout()).isEqualTo(250); - assertThat(cf.getClientConfiguration().getConnectTimeout().toMillis()).isEqualTo(1000); - }); - } - - @Test - void testRedisConfigurationWithDefaultTimeouts() { - this.contextRunner.withPropertyValues("spring.redis.host:foo").run((context) -> { + assertThat(cf.getHostName()).isEqualTo("example"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(cf.getPassword()).isEqualTo("pass:word"); + }); + } + + @Test + void testPasswordInUrlStartsWithColon() { + this.contextRunner.withPropertyValues("spring.redis.url:redis://user::pass:word@example:33").run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getTimeout()).isEqualTo(2000); - assertThat(cf.getClientConfiguration().getConnectTimeout().toMillis()).isEqualTo(2000); - }); - } - - @Test - void testRedisConfigurationWithClientName() { - this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.client-name:spring-boot") - .run((context) -> { - AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getClientName()).isEqualTo("spring-boot"); - }); - } - - //TODO question will support sentinel? - // @Test - // void testRedisConfigurationWithSentinel() { - // this.contextRunner - // .withPropertyValues("spring.redis.sentinel.master:mymaster", - // "spring.redis.sentinel.nodes:127.0.0.1:26379,127.0.0.1:26380") - // .withUserConfiguration(JedisConnectionFactoryCaptorConfiguration.class) - // .run((context) -> assertThat(JedisConnectionFactoryCaptor.connectionFactory.isRedisSentinelAware()) - // .isTrue()); - // } - - // TODO will support Sentinel? - // @Test - // void testRedisConfigurationWithSentinelAndAuthentication() { - // this.contextRunner.withPropertyValues("spring.redis.username=user", "spring.redis.password=password", - // "spring.redis.sentinel.master:mymaster", "spring.redis.sentinel.nodes:127.0.0.1:26379,127.0.0.1:26380") - // .withUserConfiguration(JedisConnectionFactoryCaptorConfiguration.class).run((context) -> { - // assertThat(JedisConnectionFactoryCaptor.connectionFactory.isRedisSentinelAware()).isTrue(); - // assertThat(JedisConnectionFactoryCaptor.connectionFactory.getPassword()).isEqualTo("password"); - // }); - // } - - // TODO will support cluster? - // @Test - // void testRedisConfigurationWithCluster() { - // this.contextRunner.withPropertyValues("spring.redis.cluster.nodes=127.0.0.1:27379,127.0.0.1:27380") - // .withUserConfiguration(JedisConnectionFactoryCaptorConfiguration.class) - // .run((context) -> assertThat(JedisConnectionFactoryCaptor.connectionFactory.isRedisClusterAware()) - // .isTrue()); - // } - - - @Configuration(proxyBeanMethods = false) - static class CustomConfiguration { - - @Bean + assertThat(cf.getHostName()).isEqualTo("example"); + assertThat(cf.getPort()).isEqualTo(33); + assertThat(cf.getPassword()).isEqualTo(":pass:word"); + }); + } + + @Test + void testRedisConfigurationWithPool() { + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.jedis.pool.min-idle:1", + "spring.redis.jedis.pool.max-idle:4", "spring.redis.jedis.pool.max-active:16", + "spring.redis.jedis.pool.max-wait:2000", "spring.redis.jedis.pool.time-between-eviction-runs:30000") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getPoolConfig().getMinIdle()).isEqualTo(1); + assertThat(cf.getPoolConfig().getMaxIdle()).isEqualTo(4); + assertThat(cf.getPoolConfig().getMaxTotal()).isEqualTo(16); + assertThat(cf.getPoolConfig().getMaxWaitDuration()).isEqualTo(Duration.ofSeconds(2)); + assertThat(cf.getPoolConfig().getDurationBetweenEvictionRuns()).isEqualTo(Duration.ofSeconds(30)); + }); + } + + @Test + void testRedisConfigurationDisabledPool() { + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.jedis.pool.enabled:false") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getClientConfiguration().isUsePooling()).isEqualTo(false); + }); + } + + @Test + void testRedisConfigurationWithTimeoutAndConnectTimeout() { + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.timeout:250", + "spring.redis.connect-timeout:1000") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getTimeout()).isEqualTo(250); + assertThat(cf.getClientConfiguration().getConnectTimeout().toMillis()).isEqualTo(1000); + }); + } + + @Test + void testRedisConfigurationWithDefaultTimeouts() { + this.contextRunner.withPropertyValues("spring.redis.host:foo") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getTimeout()).isEqualTo(2000); + assertThat(cf.getClientConfiguration().getConnectTimeout().toMillis()).isEqualTo(2000); + }); + } + + @Test + void testRedisConfigurationWithClientName() { + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.client-name:spring-boot") + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getClientName()).isEqualTo("spring-boot"); + }); + } + + @Configuration(proxyBeanMethods = false) + static class CustomConfiguration { + + @Bean JedisClientConfigurationBuilderCustomizer customizer() { - return JedisClientConfigurationBuilder::useSsl; - } + return JedisClientConfigurationBuilder::useSsl; + } - } + } - @Configuration(proxyBeanMethods = false) - static class JedisConnectionFactoryCaptorConfiguration { + @Configuration(proxyBeanMethods = false) + static class JedisConnectionFactoryCaptorConfiguration { - @Bean - JedisConnectionFactoryCaptor jedisConnectionFactoryCaptor() { - return new JedisConnectionFactoryCaptor(); - } + @Bean + JedisConnectionFactoryCaptor jedisConnectionFactoryCaptor() { + return new JedisConnectionFactoryCaptor(); + } - } + } - static class JedisConnectionFactoryCaptor implements BeanPostProcessor { + static class JedisConnectionFactoryCaptor implements BeanPostProcessor { - static JedisConnectionFactory connectionFactory; + static JedisConnectionFactory connectionFactory; - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) { - if (bean instanceof JedisConnectionFactory) { - connectionFactory = (JedisConnectionFactory) bean; - } - return bean; - } + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) { + if (bean instanceof JedisConnectionFactory) { + connectionFactory = (JedisConnectionFactory) bean; + } + return bean; + } - } + } } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java index 630fac65daf8..b1ed0faf8a55 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java @@ -101,42 +101,31 @@ public Properties toProperties() { private enum AzurePasswordlessPropertiesMapping { - scopes( - p -> p.getScopes(), + scopes(p -> p.getScopes(), (p, s) -> p.setProperty(AuthProperty.SCOPES.getPropertyKey(), s)), - clientCertificatePassword( - p -> p.getCredential().getClientCertificatePassword(), + clientCertificatePassword(p -> p.getCredential().getClientCertificatePassword(), (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PASSWORD.getPropertyKey(), s)), - clientCertificatePath( - p -> p.getCredential().getClientCertificatePath(), + clientCertificatePath(p -> p.getCredential().getClientCertificatePath(), (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PATH.getPropertyKey(), s)), - clientId( - p -> p.getCredential().getClientId(), + clientId(p -> p.getCredential().getClientId(), (p, s) -> p.setProperty(AuthProperty.CLIENT_ID.getPropertyKey(), s)), - clientSecret( - p -> p.getCredential().getClientSecret(), + clientSecret(p -> p.getCredential().getClientSecret(), (p, s) -> p.setProperty(AuthProperty.CLIENT_SECRET.getPropertyKey(), s)), - managedIdentityEnabled( - - p -> String.valueOf(p.getCredential().isManagedIdentityEnabled()), + managedIdentityEnabled(p -> String.valueOf(p.getCredential().isManagedIdentityEnabled()), (p, s) -> p.setProperty(AuthProperty.MANAGED_IDENTITY_ENABLED.getPropertyKey(), s)), - password( - p -> p.getCredential().getPassword(), + password(p -> p.getCredential().getPassword(), (p, s) -> p.setProperty(AuthProperty.PASSWORD.getPropertyKey(), s)), - username( - p -> p.getCredential().getUsername(), + username(p -> p.getCredential().getUsername(), (p, s) -> p.setProperty(AuthProperty.USERNAME.getPropertyKey(), s)), - - tenantId( - p -> p.getProfile().getTenantId(), + tenantId(p -> p.getProfile().getTenantId(), (p, s) -> p.setProperty(AuthProperty.TENANT_ID.getPropertyKey(), s)); private Function getter; diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisClientConfig.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java similarity index 97% rename from sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisClientConfig.java rename to sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java index 3dbd462853cf..823096ebef34 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisClientConfig.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.service.redis; +package com.azure.spring.cloud.service.implementation.redis; import redis.clients.jedis.HostAndPortMapper; import redis.clients.jedis.JedisClientConfig; @@ -13,6 +13,9 @@ import java.util.Objects; import java.util.function.Supplier; +/** + * JedisClientConfig implementation used for Azure Redis. + */ public final class AzureJedisClientConfig implements JedisClientConfig { private final int connectionTimeoutMillis; @@ -123,7 +126,7 @@ public static Builder builder() { return new Builder(); } - public static class Builder { + public static final class Builder { private int connectionTimeoutMillis = Protocol.DEFAULT_TIMEOUT; private int socketTimeoutMillis = Protocol.DEFAULT_TIMEOUT; diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisFactory.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactory.java similarity index 98% rename from sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisFactory.java rename to sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactory.java index b69b4e20e02d..2a2a64a68dd3 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisFactory.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactory.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.service.redis; +package com.azure.spring.cloud.service.implementation.redis; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisPool.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisPool.java similarity index 91% rename from sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisPool.java rename to sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisPool.java index e87fe142069b..b7f0405f0672 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/AzureJedisPool.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisPool.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.service.redis; +package com.azure.spring.cloud.service.implementation.redis; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.HostAndPort; diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/package-info.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/package-info.java deleted file mode 100644 index 6d9d5cc2a059..000000000000 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/redis/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.spring.cloud.service.redis; diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/redis/AzureJedisPoolTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/redis/AzureJedisPoolTest.java deleted file mode 100644 index c06952315fc2..000000000000 --- a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/redis/AzureJedisPoolTest.java +++ /dev/null @@ -1,533 +0,0 @@ -//package com.azure.spring.cloud.service.redis; -// -//import org.apache.commons.pool2.PooledObject; -//import org.apache.commons.pool2.PooledObjectFactory; -//import org.apache.commons.pool2.impl.DefaultPooledObject; -//import org.apache.commons.pool2.impl.GenericObjectPoolConfig; -//import org.junit.jupiter.api.Assertions; -//import org.junit.jupiter.api.Test; -//import redis.clients.jedis.DefaultJedisClientConfig; -//import redis.clients.jedis.HostAndPort; -//import redis.clients.jedis.Jedis; -//import redis.clients.jedis.JedisFactory; -//import redis.clients.jedis.JedisPool; -//import redis.clients.jedis.JedisPoolConfig; -//import redis.clients.jedis.Protocol; -//import redis.clients.jedis.Transaction; -//import redis.clients.jedis.exceptions.InvalidURIException; -//import redis.clients.jedis.exceptions.JedisConnectionException; -//import redis.clients.jedis.exceptions.JedisException; -// -//import java.net.URI; -//import java.net.URISyntaxException; -//import java.util.ArrayList; -//import java.util.List; -//import java.util.concurrent.atomic.AtomicInteger; -// -//import static org.junit.jupiter.api.Assertions.assertEquals; -//import static org.junit.jupiter.api.Assertions.assertNull; -//import static org.junit.jupiter.api.Assertions.assertSame; -//import static org.junit.jupiter.api.Assertions.assertTrue; -//import static org.junit.jupiter.api.Assertions.fail; -// -//public class AzureJedisPoolTest { -// -// private static final HostAndPort hnp = HostAndPorts.getRedisServers().get(0); -// -// @Test -// public void checkConnections() { -// JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000); -// try (Jedis jedis = pool.getResource()) { -// jedis.auth("foobared"); -// jedis.set("foo", "bar"); -// assertEquals("bar", jedis.get("foo")); -// } -// pool.close(); -// assertTrue(pool.isClosed()); -// } -// -// @Test -// public void checkResourceWithConfig() { -// try (JedisPool pool = new JedisPool(HostAndPorts.getRedisServers().get(7), -// DefaultJedisClientConfig.builder().socketTimeoutMillis(5000).build())) { -// -// try (Jedis jedis = pool.getResource()) { -// assertEquals("PONG", jedis.ping()); -// assertEquals(5000, jedis.getClient().getSoTimeout()); -// jedis.close(); -// } -// } -// } -// -// @Test -// public void checkCloseableConnections() throws Exception { -// JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000); -// try (Jedis jedis = pool.getResource()) { -// jedis.auth("foobared"); -// jedis.set("foo", "bar"); -// assertEquals("bar", jedis.get("foo")); -// } -// pool.close(); -// assertTrue(pool.isClosed()); -// } -// -// @Test -// public void checkConnectionWithDefaultHostAndPort() { -// JedisPool pool = new JedisPool(new JedisPoolConfig()); -// try (Jedis jedis = pool.getResource()) { -// jedis.auth("foobared"); -// jedis.set("foo", "bar"); -// assertEquals("bar", jedis.get("foo")); -// } -// pool.close(); -// assertTrue(pool.isClosed()); -// } -// -// @Test -// public void checkResourceIsClosableAndReusable() { -// GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); -// config.setMaxTotal(1); -// config.setBlockWhenExhausted(false); -// try (JedisPool pool = new JedisPool(config, hnp.getHost(), hnp.getPort(), 2000, "foobared", 0, -// "closable-resuable-pool", false, null, null, null)) { -// -// Jedis jedis = pool.getResource(); -// jedis.set("hello", "jedis"); -// jedis.close(); -// -// Jedis jedis2 = pool.getResource(); -// assertEquals(jedis, jedis2); -// assertEquals("jedis", jedis2.get("hello")); -// jedis2.close(); -// } -// } -// -// @Test -// public void checkPoolRepairedWhenJedisIsBroken() { -// JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort()); -// try (Jedis jedis = pool.getResource()) { -// jedis.auth("foobared"); -// jedis.set("foo", "0"); -// jedis.quit(); -// } -// -// try (Jedis jedis = pool.getResource()) { -// jedis.auth("foobared"); -// jedis.incr("foo"); -// } -// pool.close(); -// assertTrue(pool.isClosed()); -// } -// -// @Test -// public void checkPoolOverflow() { -// Assertions.assertThrows(JedisException.class, () -> { -// GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); -// config.setMaxTotal(1); -// config.setBlockWhenExhausted(false); -// try (JedisPool pool = new JedisPool(config, hnp.getHost(), hnp.getPort()); -// Jedis jedis = pool.getResource()) { -// jedis.auth("foobared"); -// -// try (Jedis jedis2 = pool.getResource()) { -// jedis2.auth("foobared"); -// } -// } -// }); -// } -// -// @Test -// public void securePool() { -// JedisPoolConfig config = new JedisPoolConfig(); -// config.setTestOnBorrow(true); -// JedisPool pool = new JedisPool(config, hnp.getHost(), hnp.getPort(), 2000, "foobared"); -// try (Jedis jedis = pool.getResource()) { -// jedis.set("foo", "bar"); -// } -// pool.close(); -// assertTrue(pool.isClosed()); -// } -// -// @Test -// public void nonDefaultDatabase() { -// try (JedisPool pool0 = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, -// "foobared"); Jedis jedis0 = pool0.getResource()) { -// jedis0.set("foo", "bar"); -// assertEquals("bar", jedis0.get("foo")); -// } -// -// try (JedisPool pool1 = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, -// "foobared", 1); Jedis jedis1 = pool1.getResource()) { -// assertNull(jedis1.get("foo")); -// } -// } -// -// @Test -// public void startWithUrlString() { -// try (Jedis j = new Jedis("localhost", 6380)) { -// j.auth("foobared"); -// j.select(2); -// j.set("foo", "bar"); -// } -// -// try (JedisPool pool = new JedisPool("redis://:foobared@localhost:6380/2"); -// Jedis jedis = pool.getResource()) { -// assertEquals("PONG", jedis.ping()); -// assertEquals("bar", jedis.get("foo")); -// } -// } -// -// @Test -// public void startWithUrl() throws URISyntaxException { -// try (Jedis j = new Jedis("localhost", 6380)) { -// j.auth("foobared"); -// j.select(2); -// j.set("foo", "bar"); -// } -// -// try (JedisPool pool = new JedisPool(new URI("redis://:foobared@localhost:6380/2")); -// Jedis jedis = pool.getResource()) { -// assertEquals("bar", jedis.get("foo")); -// } -// } -// -// @Test -// public void shouldThrowInvalidURIExceptionForInvalidURI() throws URISyntaxException { -// Assertions.assertThrows(InvalidURIException.class, () -> { -// new JedisPool(new URI("localhost:6380")).close(); -// }); -// } -// -// @Test -// public void allowUrlWithNoDBAndNoPassword() throws URISyntaxException { -// new JedisPool("redis://localhost:6380").close(); -// new JedisPool(new URI("redis://localhost:6380")).close(); -// } -// -// @Test -// public void selectDatabaseOnActivation() { -// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, -// "foobared")) { -// -// Jedis jedis0 = pool.getResource(); -// assertEquals(0, jedis0.getDB()); -// -// jedis0.select(1); -// assertEquals(1, jedis0.getDB()); -// -// jedis0.close(); -// -// Jedis jedis1 = pool.getResource(); -// assertTrue(jedis1 == jedis0, "Jedis instance was not reused"); -// assertEquals(0, jedis1.getDB()); -// -// jedis1.close(); -// } -// } -// -// @Test -// public void customClientName() { -// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, -// "foobared", 0, "my_shiny_client_name"); Jedis jedis = pool.getResource()) { -// -// assertEquals("my_shiny_client_name", jedis.clientGetname()); -// } -// } -// -// @Test -// public void returnResourceDestroysResourceOnException() { -// -// class CrashingJedis extends Jedis { -// @Override -// public void resetState() { -// throw new RuntimeException(); -// } -// } -// -// final AtomicInteger destroyed = new AtomicInteger(0); -// -// class CrashingJedisPooledObjectFactory implements PooledObjectFactory { -// -// @Override -// public PooledObject makeObject() throws Exception { -// return new DefaultPooledObject(new CrashingJedis()); -// } -// -// @Override -// public void destroyObject(PooledObject p) throws Exception { -// destroyed.incrementAndGet(); -// } -// -// @Override -// public boolean validateObject(PooledObject p) { -// return true; -// } -// -// @Override -// public void activateObject(PooledObject p) throws Exception { -// } -// -// @Override -// public void passivateObject(PooledObject p) throws Exception { -// } -// } -// -// GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); -// config.setMaxTotal(1); -// JedisPool pool = new JedisPool(config, new CrashingJedisPooledObjectFactory()); -// Jedis crashingJedis = pool.getResource(); -// -// try { -// crashingJedis.close(); -// } catch (Exception ignored) { -// } -// -// assertEquals(1, destroyed.get()); -// } -// -// @Test -// public void returnResourceShouldResetState() { -// GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); -// config.setMaxTotal(1); -// config.setBlockWhenExhausted(false); -// JedisPool pool = new JedisPool(config, hnp.getHost(), hnp.getPort(), 2000, "foobared"); -// -// Jedis jedis = pool.getResource(); -// try { -// jedis.set("hello", "jedis"); -// Transaction t = jedis.multi(); -// t.set("hello", "world"); -// } finally { -// jedis.close(); -// } -// -// Jedis jedis2 = pool.getResource(); -// try { -// assertTrue(jedis == jedis2); -// assertEquals("jedis", jedis2.get("hello")); -// } finally { -// jedis2.close(); -// } -// -// pool.close(); -// assertTrue(pool.isClosed()); -// } -// -// @Test -// public void getNumActiveWhenPoolIsClosed() { -// JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, -// "foobared", 0, "my_shiny_client_name"); -// -// try (Jedis j = pool.getResource()) { -// j.ping(); -// } -// -// pool.close(); -// assertEquals(0, pool.getNumActive()); -// } -// -// @Test -// public void getNumActiveReturnsTheCorrectNumber() { -// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000)) { -// Jedis jedis = pool.getResource(); -// jedis.auth("foobared"); -// jedis.set("foo", "bar"); -// assertEquals("bar", jedis.get("foo")); -// -// assertEquals(1, pool.getNumActive()); -// -// Jedis jedis2 = pool.getResource(); -// jedis.auth("foobared"); -// jedis.set("foo", "bar"); -// -// assertEquals(2, pool.getNumActive()); -// -// jedis.close(); -// assertEquals(1, pool.getNumActive()); -// -// jedis2.close(); -// -// assertEquals(0, pool.getNumActive()); -// } -// } -// -// @Test -// public void testAddObject() { -// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000)) { -// pool.addObjects(1); -// assertEquals(1, pool.getNumIdle()); -// } -// } -// -// @Test -// public void closeResourceTwice() { -// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000)) { -// Jedis j = pool.getResource(); -// j.auth("foobared"); -// j.ping(); -// j.close(); -// j.close(); -// } -// } -// -// @Test -// public void closeBrokenResourceTwice() { -// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000)) { -// Jedis j = pool.getResource(); -// try { -// // make connection broken -// j.getClient().getOne(); -// fail(); -// } catch (Exception e) { -// assertTrue(e instanceof JedisConnectionException); -// } -// assertTrue(j.isBroken()); -// j.close(); -// j.close(); -// } -// } -// -// @Test -// public void testCloseConnectionOnMakeObject() { -// JedisPoolConfig config = new JedisPoolConfig(); -// config.setTestOnBorrow(true); -// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), hnp.getPort(), 2000, -// "wrong pass"); Jedis jedis = new Jedis("redis://:foobared@localhost:6379/")) { -// int currentClientCount = getClientCount(jedis.clientList()); -// try { -// pool.getResource(); -// fail("Should throw exception as password is incorrect."); -// } catch (Exception e) { -// assertEquals(currentClientCount, getClientCount(jedis.clientList())); -// } -// } -// } -// -// private int getClientCount(final String clientList) { -// return clientList.split("\n").length; -// } -// -// @Test -// public void testResetInvalidPassword() { -// JedisFactory factory = new JedisFactory(hnp.getHost(), hnp.getPort(), 2000, 2000, -// "foobared", 0, "my_shiny_client_name") { -// }; -// -// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), factory)) { -// Jedis obj1_ref; -// try (Jedis obj1_1 = pool.getResource()) { -// obj1_ref = obj1_1; -// obj1_1.set("foo", "bar"); -// assertEquals("bar", obj1_1.get("foo")); -// assertEquals(1, pool.getNumActive()); -// } -// assertEquals(0, pool.getNumActive()); -// try (Jedis obj1_2 = pool.getResource()) { -// assertSame(obj1_ref, obj1_2); -// assertEquals(1, pool.getNumActive()); -// factory.setPassword("wrong password"); -// try (Jedis obj2 = pool.getResource()) { -// fail("Should not get resource from pool"); -// } catch (JedisException e) { -// } -// assertEquals(1, pool.getNumActive()); -// } -// assertEquals(0, pool.getNumActive()); -// } -// } -// -// @Test -// public void testResetValidPassword() { -// JedisFactory factory = new JedisFactory(hnp.getHost(), hnp.getPort(), 2000, 2000, -// "bad password", 0, "my_shiny_client_name") { -// }; -// -// try (JedisPool pool = new JedisPool(new JedisPoolConfig(), factory)) { -// try (Jedis obj1 = pool.getResource()) { -// fail("Should not get resource from pool"); -// } catch (JedisException e) { -// } -// assertEquals(0, pool.getNumActive()); -// -// factory.setPassword("foobared"); -// try (Jedis obj2 = pool.getResource()) { -// obj2.set("foo", "bar"); -// assertEquals("bar", obj2.get("foo")); -// } -// } -// } -// -// private final class HostAndPorts { -// -// private static List redisHostAndPortList = new ArrayList<>(); -// private static List sentinelHostAndPortList = new ArrayList<>(); -// private static List clusterHostAndPortList = new ArrayList<>(); -// -// static { -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT)); -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 1)); -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 2)); -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 3)); -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 4)); -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 5)); -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 6)); -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 7)); -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 8)); -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 9)); -// redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 10)); -// -// sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT)); -// sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 1)); -// sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 2)); -// sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 3)); -// sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 4)); -// -// clusterHostAndPortList.add(new HostAndPort("localhost", 7379)); -// clusterHostAndPortList.add(new HostAndPort("localhost", 7380)); -// clusterHostAndPortList.add(new HostAndPort("localhost", 7381)); -// clusterHostAndPortList.add(new HostAndPort("localhost", 7382)); -// clusterHostAndPortList.add(new HostAndPort("localhost", 7383)); -// clusterHostAndPortList.add(new HostAndPort("localhost", 7384)); -// } -// -// public static List parseHosts(String envHosts, -// List existingHostsAndPorts) { -// -// if (null != envHosts && 0 < envHosts.length()) { -// -// String[] hostDefs = envHosts.split(","); -// -// if (null != hostDefs && 2 <= hostDefs.length) { -// -// List envHostsAndPorts = new ArrayList<>(hostDefs.length); -// -// for (String hostDef : hostDefs) { -// -// envHostsAndPorts.add(HostAndPort.from(hostDef)); -// } -// -// return envHostsAndPorts; -// } -// } -// -// return existingHostsAndPorts; -// } -// -// public static List getRedisServers() { -// return redisHostAndPortList; -// } -// -// public static List getSentinelServers() { -// return sentinelHostAndPortList; -// } -// -// public static List getClusterServers() { -// return clusterHostAndPortList; -// } -// -// private HostAndPorts() { -// throw new InstantiationError("Must not instantiate this class"); -// } -// } -// -//} From e71f7d600997f2884b280f8bbde1e2fd99270a56 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Thu, 5 Jan 2023 20:08:54 +0800 Subject: [PATCH 03/38] update name --- ...a => AzureJedisPasswordlessConnectionConfigurationTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/{AzureJedisPasswordlessAutoConfigurationTest.java => AzureJedisPasswordlessConnectionConfigurationTest.java} (99%) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfigurationTest.java similarity index 99% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfigurationTest.java index 3648b746d0d2..f99f73881403 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfigurationTest.java @@ -20,7 +20,7 @@ /** * Tests for {@link AzureJedisPasswordlessConnectionConfiguration} when Lettuce is not on the classpath. */ -class AzureJedisPasswordlessAutoConfigurationTest { +class AzureJedisPasswordlessConnectionConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withPropertyValues( From f6660ccadc37a403c32c19dd1e54370fa83e19ec Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Fri, 6 Jan 2023 14:37:45 +0800 Subject: [PATCH 04/38] fix pipeline --- sdk/identity/azure-identity-extensions/pom.xml | 8 -------- .../extensions/implementation/enums/AuthProperty.java | 2 +- sdk/spring/spring-cloud-azure-service/pom.xml | 1 + 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/sdk/identity/azure-identity-extensions/pom.xml b/sdk/identity/azure-identity-extensions/pom.xml index 3b8e5fd8d440..b50776f978e3 100644 --- a/sdk/identity/azure-identity-extensions/pom.xml +++ b/sdk/identity/azure-identity-extensions/pom.xml @@ -92,14 +92,6 @@ false - - org.apache.maven.plugins - maven-compiler-plugin - - 9 - 9 - - diff --git a/sdk/identity/azure-identity-extensions/src/main/java/com/azure/identity/extensions/implementation/enums/AuthProperty.java b/sdk/identity/azure-identity-extensions/src/main/java/com/azure/identity/extensions/implementation/enums/AuthProperty.java index c8f3806cd1da..f7a39a37ece0 100644 --- a/sdk/identity/azure-identity-extensions/src/main/java/com/azure/identity/extensions/implementation/enums/AuthProperty.java +++ b/sdk/identity/azure-identity-extensions/src/main/java/com/azure/identity/extensions/implementation/enums/AuthProperty.java @@ -83,7 +83,7 @@ public enum AuthProperty { "Scopes for Azure resources.", false), /** - * Max time to get an access token.SCOPES + * Max time to get an access token. */ GET_TOKEN_TIMEOUT("azure.accessTokenTimeoutInSeconds", "Max time to get an access token.", diff --git a/sdk/spring/spring-cloud-azure-service/pom.xml b/sdk/spring/spring-cloud-azure-service/pom.xml index 716f9f7f7383..cd192934fa30 100644 --- a/sdk/spring/spring-cloud-azure-service/pom.xml +++ b/sdk/spring/spring-cloud-azure-service/pom.xml @@ -162,6 +162,7 @@ org.apache.kafka:kafka-clients:[3.1.2] + redis.clients:jedis:[3.8.0] From 60a0c78ae03c8eec71a2cbf81267e346ba122537 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Fri, 6 Jan 2023 19:32:38 +0800 Subject: [PATCH 05/38] add tests --- .../jedis/AzureJedisConnectionFactory.java | 16 +- ...reJedisPasswordlessAutoConfiguration.java} | 9 +- .../jedis/AzureRedisCredentialSupplier.java | 17 +- .../main/resources/META-INF/spring.factories | 2 +- .../AzureJedisConnectionFactoryTest.java | 232 ++++++++++++++++++ ...disPasswordlessAutoConfigurationTest.java} | 22 +- .../AzureRedisCredentialSupplierTest.java | 22 ++ .../AzureRedisPasswordlessProperties.java | 3 +- .../redis/AzureJedisClientConfig.java | 16 +- .../redis/AzureJedisClientConfigTest.java | 110 +++++++++ .../redis/AzureJedisFactoryTest.java | 117 +++++++++ 11 files changed, 538 insertions(+), 28 deletions(-) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/{AzureJedisPasswordlessConnectionConfiguration.java => AzureJedisPasswordlessAutoConfiguration.java} (97%) create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/{AzureJedisPasswordlessConnectionConfigurationTest.java => AzureJedisPasswordlessAutoConfigurationTest.java} (96%) create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java create mode 100644 sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfigTest.java create mode 100644 sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactoryTest.java diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java index ecbe6b910beb..0091e76096b2 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -42,7 +42,7 @@ public class AzureJedisConnectionFactory implements InitializingBean, Disposable private final RedisStandaloneConfiguration standaloneConfig; - private final Supplier credentialSupplier; + private final Supplier credentialSupplier; private final JedisClientConfiguration clientConfiguration; private JedisClientConfig jedisClientConfig; private @Nullable Pool pool; @@ -51,7 +51,6 @@ public class AzureJedisConnectionFactory implements InitializingBean, Disposable private boolean convertPipelineAndTxResults = true; - /** * Constructs a new AzureJedisConnectionFactory instance using the given RedisStandaloneConfiguration, JedisClientConfiguration and CredentialSupplier. * @@ -59,7 +58,7 @@ public class AzureJedisConnectionFactory implements InitializingBean, Disposable * @param clientConfiguration must not be {@literal null}. * @param credentialSupplier must not be {@literal null}. */ - public AzureJedisConnectionFactory(RedisStandaloneConfiguration standaloneConfig, JedisClientConfiguration clientConfiguration, Supplier credentialSupplier) { + public AzureJedisConnectionFactory(RedisStandaloneConfiguration standaloneConfig, JedisClientConfiguration clientConfiguration, Supplier credentialSupplier) { this.standaloneConfig = standaloneConfig; this.clientConfiguration = clientConfiguration; this.credentialSupplier = credentialSupplier; @@ -107,7 +106,7 @@ public void destroy() { @Override public DataAccessException translateExceptionIfPossible(RuntimeException ex) { - return null; + throw new UnsupportedOperationException("This operation is not supported"); } @Override @@ -186,7 +185,11 @@ public String getHostName() { */ @Nullable public String getPassword() { - return getRedisPassword().map(String::new).orElse(null); + RedisPassword password = getRedisPassword(); + if (password == null & credentialSupplier != null) { + return credentialSupplier.get(); + } + return password.map(String::new).orElse(null); } /** @@ -314,4 +317,7 @@ private void assertInitialized() { Assert.state(!this.destroyed, "JedisConnectionFactory was destroyed and cannot be used anymore"); } + void setPool(@Nullable Pool pool) { + this.pool = pool; + } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java similarity index 97% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java index bf3b9fedebca..497ab9d6254c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java @@ -47,10 +47,10 @@ @ConditionalOnMissingBean(RedisConnectionFactory.class) @ConditionalOnProperty(prefix = "spring.redis", name = {"host", "username"}) @EnableConfigurationProperties(RedisProperties.class) -class AzureJedisPasswordlessConnectionConfiguration { +class AzureJedisPasswordlessAutoConfiguration { private static final boolean COMMONS_POOL2_AVAILABLE = ClassUtils.isPresent("org.apache.commons.pool2.ObjectPool", - AzureJedisPasswordlessConnectionConfiguration.class.getClassLoader()); + AzureJedisPasswordlessAutoConfiguration.class.getClassLoader()); private static final String AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME = "azureRedisCredentialSupplier"; @@ -64,20 +64,19 @@ AzureRedisPasswordlessProperties redisPasswordlessProperties() { @Bean(name = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) @ConditionalOnMissingBean(name = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) - Supplier azureRedisCredentialSupplier(ObjectProvider azureGlobalProperties, AzureRedisPasswordlessProperties azureRedisPasswordlessProperties) { + Supplier azureRedisCredentialSupplier(ObjectProvider azureGlobalProperties, AzureRedisPasswordlessProperties azureRedisPasswordlessProperties) { Properties properties = mergeAzureProperties(azureGlobalProperties.getIfAvailable(), azureRedisPasswordlessProperties).toProperties(); return new AzureRedisCredentialSupplier(properties); } @Bean - AzureJedisConnectionFactory azureRedisConnectionFactory(RedisProperties redisProperties, @Qualifier(value = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) Supplier azureRedisCredentialSupplier) { + AzureJedisConnectionFactory azureRedisConnectionFactory(RedisProperties redisProperties, @Qualifier(value = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) Supplier azureRedisCredentialSupplier) { RedisStandaloneConfiguration standaloneConfig = getStandaloneConfig(redisProperties); JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(redisProperties); return new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, azureRedisCredentialSupplier); } - private JedisClientConfiguration getJedisClientConfiguration(RedisProperties redisProperties) { JedisClientConfiguration.JedisClientConfigurationBuilder builder = applyProperties(redisProperties, JedisClientConfiguration.builder()); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java index 4dc481f35daf..6537a7b814af 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java @@ -12,11 +12,11 @@ import java.util.function.Supplier; /** - * AzureRedisCredentialSupplier that provide a char array as the password to connect Azure Redis. + * AzureRedisCredentialSupplier that provide a String as the password to connect Azure Redis. * * @since 4.6.0 */ -public class AzureRedisCredentialSupplier implements Supplier { +public class AzureRedisCredentialSupplier implements Supplier { private final AzureAuthenticationTemplate azureAuthenticationTemplate; @@ -42,17 +42,20 @@ public AzureRedisCredentialSupplier(Properties properties, TokenCredentialProvid /** * Create {@link AzureRedisCredentialSupplier} instance. - * @param tokenCredentialProvider Supplier that provide a {@link TokenCredential}. + * @param tokenCredentialProvider Supplier that provide a {@link TokenCredential}, must not be {@literal null}. * @param accessTokenResolver An {@link AccessTokenResolver} instance, which will take a TokenCredential as input - * and outputs a publisher that emits a single access token. + * and outputs a publisher that emits a single access token, must not be {@literal null}. */ public AzureRedisCredentialSupplier(TokenCredentialProvider tokenCredentialProvider, AccessTokenResolver accessTokenResolver) { - azureAuthenticationTemplate = new AzureAuthenticationTemplate(tokenCredentialProvider, accessTokenResolver); + this(null, tokenCredentialProvider, accessTokenResolver); } @Override - public char[] get() { - return azureAuthenticationTemplate.getTokenAsPassword().toCharArray(); + public String get() { + return azureAuthenticationTemplate.getTokenAsPassword(); } + AzureRedisCredentialSupplier(AzureAuthenticationTemplate azureAuthenticationTemplate) { + this.azureAuthenticationTemplate = azureAuthenticationTemplate; + } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories index 9ce2e51fb823..4e09564aa2f7 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories @@ -39,7 +39,7 @@ com.azure.spring.cloud.autoconfigure.resourcemanager.AzureServiceBusResourceMana com.azure.spring.cloud.autoconfigure.resourcemanager.AzureStorageQueueResourceManagerAutoConfiguration,\ com.azure.spring.cloud.autoconfigure.compatibility.AzureCompatibilityVerifierAutoConfiguration,\ com.azure.spring.cloud.autoconfigure.jdbc.AzureJdbcAutoConfiguration,\ -com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis.AzureJedisPasswordlessConnectionConfiguration +com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis.AzureJedisPasswordlessAutoConfiguration org.springframework.boot.diagnostics.FailureAnalyzer=\ com.azure.spring.cloud.autoconfigure.implementation.compatibility.AzureCompatibilityNotMetFailureAnalyzer diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java new file mode 100644 index 000000000000..d116be5a6e6c --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java @@ -0,0 +1,232 @@ +package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; + +import com.azure.spring.cloud.core.implementation.util.ReflectionUtils; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.data.redis.connection.RedisPassword; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.util.Pool; + +import java.time.Duration; +import java.util.Optional; +import java.util.function.Supplier; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class AzureJedisConnectionFactoryTest { + + RedisStandaloneConfiguration standaloneConfig; + JedisClientConfiguration clientConfiguration; + Supplier credentialSupplier; + + @Test + void getConnectionWithoutInit() { + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertThrows(IllegalStateException.class, () -> { + azureJedisConnectionFactory.getConnection(); + }); + } + + // + @Test + void getConnection() { + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + azureJedisConnectionFactory.getConnection(); + } + + @Test + void afterPropertiesSet() { + } + + @Test + void postProcessConnection() { + } + + @Test + void createRedisPool() { + } + + @Test + void fetchJedisConnector() { + } + + @Test + void testDestroyWithPool() { + + clientConfiguration = mock(JedisClientConfiguration.class); + when(clientConfiguration.isUsePooling()).thenReturn(true); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Pool mockPool = mock(Pool.class); + azureJedisConnectionFactory.setPool(mockPool); + + Boolean destroyed = (Boolean) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "destroyed", azureJedisConnectionFactory); + Pool pool = (Pool) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "pool", azureJedisConnectionFactory); + + Assertions.assertFalse(destroyed); + Assertions.assertEquals(mockPool, pool); + + azureJedisConnectionFactory.destroy(); + + destroyed = (Boolean) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "destroyed", azureJedisConnectionFactory); + pool = (Pool) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "pool", azureJedisConnectionFactory); + + Assertions.assertTrue(destroyed); + Assertions.assertNull(pool); + + } + + @Test + void translateExceptionIfPossible() { + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertThrows(UnsupportedOperationException.class, () -> { + azureJedisConnectionFactory.translateExceptionIfPossible(new RuntimeException()); + }); + + } + + + @Test + void getClusterConnection() { + + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + + Assertions.assertThrows(UnsupportedOperationException.class, () -> { + azureJedisConnectionFactory.getClusterConnection(); + }); + } + + @Test + void getPoolConfig() { + clientConfiguration = mock(JedisClientConfiguration.class); + GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig<>(); + when(clientConfiguration.getPoolConfig()).thenReturn(Optional.of(poolConfig)); + + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertEquals(poolConfig, azureJedisConnectionFactory.getPoolConfig()); + } + + @Test + void getConvertPipelineAndTxResults() { + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertTrue(azureJedisConnectionFactory.getConvertPipelineAndTxResults()); + } + + @Test + void getSentinelConnection() { + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertThrows(UnsupportedOperationException.class, () -> { + azureJedisConnectionFactory.getSentinelConnection(); + }); + } + + @Test + void getUsePool() { + clientConfiguration = mock(JedisClientConfiguration.class); + when(clientConfiguration.isUsePooling()).thenReturn(true); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertTrue(azureJedisConnectionFactory.getUsePool()); + + clientConfiguration = mock(JedisClientConfiguration.class); + when(clientConfiguration.isUsePooling()).thenReturn(false); + azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertFalse(azureJedisConnectionFactory.getUsePool()); + } + + @Test + void getDatabase() { + standaloneConfig = mock(RedisStandaloneConfiguration.class); + when(standaloneConfig.getDatabase()).thenReturn(7); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertEquals(7, azureJedisConnectionFactory.getDatabase()); + } + + @Test + void getClientConfiguration() { + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + + JedisClientConfiguration configuration = azureJedisConnectionFactory.getClientConfiguration(); + Assertions.assertNull(configuration); + + clientConfiguration = mock(JedisClientConfiguration.class); + azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertTrue(clientConfiguration == azureJedisConnectionFactory.getClientConfiguration()); + + } + + @Test + void setConvertPipelineAndTxResults() { + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + + azureJedisConnectionFactory.setConvertPipelineAndTxResults(false); + Assertions.assertFalse(azureJedisConnectionFactory.getConvertPipelineAndTxResults()); + + azureJedisConnectionFactory.setConvertPipelineAndTxResults(true); + Assertions.assertTrue(azureJedisConnectionFactory.getConvertPipelineAndTxResults()); + } + + @Test + void getPort() { + standaloneConfig = mock(RedisStandaloneConfiguration.class); + when(standaloneConfig.getPort()).thenReturn(1717); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertEquals(1717, azureJedisConnectionFactory.getPort()); + } + + @Test + void isUseSsl() { + clientConfiguration = mock(JedisClientConfiguration.class); + + when(clientConfiguration.isUseSsl()).thenReturn(true); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertEquals(true, azureJedisConnectionFactory.isUseSsl()); + + when(clientConfiguration.isUseSsl()).thenReturn(false); + azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertEquals(false, azureJedisConnectionFactory.isUseSsl()); + } + + @Test + void getHostName() { + standaloneConfig = mock(RedisStandaloneConfiguration.class); + when(standaloneConfig.getHostName()).thenReturn("fake-host-name"); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertEquals("fake-host-name", azureJedisConnectionFactory.getHostName()); + } + + @Test + void getPasswordFromConfig() { + standaloneConfig = mock(RedisStandaloneConfiguration.class); + when(standaloneConfig.getPassword()).thenReturn(RedisPassword.of("password-from-config")); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertEquals("password-from-config", azureJedisConnectionFactory.getPassword()); + } + + @Test + void getPasswordFromSupplier() { + standaloneConfig = mock(RedisStandaloneConfiguration.class); + credentialSupplier = mock(Supplier.class); + when(credentialSupplier.get()).thenReturn("password-from-credential-supplier"); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertEquals("password-from-credential-supplier", azureJedisConnectionFactory.getPassword()); + } + + @Test + void getClientName() { + clientConfiguration = mock(JedisClientConfiguration.class); + when(clientConfiguration.getClientName()).thenReturn(Optional.of("fake-clientName")); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertEquals("fake-clientName", azureJedisConnectionFactory.getClientName()); + } + + @Test + void getTimeout() { + clientConfiguration = mock(JedisClientConfiguration.class); + when(clientConfiguration.getReadTimeout()).thenReturn(Duration.ofSeconds(23)); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertEquals(23000, azureJedisConnectionFactory.getTimeout()); + } +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java similarity index 96% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfigurationTest.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java index f99f73881403..25584a3c8382 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessConnectionConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -18,9 +18,9 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link AzureJedisPasswordlessConnectionConfiguration} when Lettuce is not on the classpath. + * Tests for {@link AzureJedisPasswordlessAutoConfiguration} when Lettuce is not on the classpath. */ -class AzureJedisPasswordlessConnectionConfigurationTest { +class AzureJedisPasswordlessAutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withPropertyValues( @@ -28,7 +28,22 @@ class AzureJedisPasswordlessConnectionConfigurationTest { "spring.redis.username = testuser", "spring.redis.host = testhost" ) - .withConfiguration(AutoConfigurations.of(AzureJedisPasswordlessConnectionConfiguration.class)); + .withConfiguration(AutoConfigurations.of(AzureJedisPasswordlessAutoConfiguration.class)); + + @Test + void testScopes() { + + } + + @Test + void testCredentialSupplier() { + + } + + @Test + void testProperties() { + + } @Test void connectionFactoryDefaultsToJedis() { @@ -36,7 +51,6 @@ void connectionFactoryDefaultsToJedis() { .isInstanceOf(AzureJedisConnectionFactory.class)); } - @Test void testOverrideRedisConfiguration() { this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1").run((context) -> { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java new file mode 100644 index 000000000000..d5f6c89ce75c --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java @@ -0,0 +1,22 @@ +package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; + + +import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class AzureRedisCredentialSupplierTest { + + @Test + void testGet() { + AzureAuthenticationTemplate template = mock(AzureAuthenticationTemplate.class); + when(template.getTokenAsPassword()).thenReturn("fake-password"); + + AzureRedisCredentialSupplier supplier = new AzureRedisCredentialSupplier(template); + Assertions.assertEquals("fake-password", supplier.get()); + } + +} diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java index d0e9630dc28e..be82b243d07c 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java @@ -4,11 +4,12 @@ package com.azure.spring.cloud.service.implementation.passwordless; import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; + import java.util.HashMap; import java.util.Map; /** - * + * Configuration properties for passwordless connections with Azure Redis. */ public class AzureRedisPasswordlessProperties extends AzurePasswordlessProperties { diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java index 823096ebef34..3a2f1acf66a0 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java @@ -34,12 +34,12 @@ public final class AzureJedisClientConfig implements JedisClientConfig { private final HostAndPortMapper hostAndPortMapper; - private Supplier credentialSupplier; + private Supplier credentialSupplier ; private AzureJedisClientConfig(int connectionTimeoutMillis, int soTimeoutMillis, int blockingSocketTimeoutMillis, String user, String password, int database, String clientName, boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, - HostnameVerifier hostnameVerifier, HostAndPortMapper hostAndPortMapper, Supplier credentialSupplier) { + HostnameVerifier hostnameVerifier, HostAndPortMapper hostAndPortMapper, Supplier credentialSupplier) { this.connectionTimeoutMillis = connectionTimeoutMillis; this.socketTimeoutMillis = soTimeoutMillis; this.blockingSocketTimeoutMillis = blockingSocketTimeoutMillis; @@ -77,7 +77,13 @@ public String getUser() { @Override public String getPassword() { - return this.password != null ? this.password : new String(this.credentialSupplier.get()); + if (this.password != null) { + return this.password; + } + if (this.credentialSupplier != null) { + return this.credentialSupplier.get(); + } + return this.password; } @Override @@ -135,7 +141,7 @@ public static final class Builder { private String user = null; private String password = null; - private Supplier credentialSupplier = null; + private Supplier credentialSupplier = null; private int database = Protocol.DEFAULT_DATABASE; private String clientName = null; @@ -180,7 +186,7 @@ public Builder password(String password) { return this; } - public Builder credentialSupplier(Supplier credentialSupplier) { + public Builder credentialSupplier(Supplier credentialSupplier) { this.credentialSupplier = credentialSupplier; return this; } diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfigTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfigTest.java new file mode 100644 index 000000000000..04c8757fd6f4 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfigTest.java @@ -0,0 +1,110 @@ +package com.azure.spring.cloud.service.implementation.redis; + + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import redis.clients.jedis.HostAndPortMapper; +import redis.clients.jedis.Protocol; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocketFactory; + +import static org.mockito.Mockito.mock; + +class AzureJedisClientConfigTest { + + AzureJedisClientConfig.Builder builder = AzureJedisClientConfig.builder(); + + @Test + void testCustomerParams() { + SSLSocketFactory mockSslSocketFactory = mock(SSLSocketFactory.class); + SSLParameters mockSslParameters = mock(SSLParameters.class); + HostnameVerifier mockHostnameVerifier = mock(HostnameVerifier.class); + HostAndPortMapper mockHostAndPortMapper = mock(HostAndPortMapper.class); + + builder.user("fake-user"); + builder.database(1); + builder.clientName("fake-client-name"); + builder.connectionTimeoutMillis(1500); + builder.socketTimeoutMillis(2000); + builder.blockingSocketTimeoutMillis(1400); + builder.ssl(false); + builder.sslSocketFactory(mockSslSocketFactory); + builder.sslParameters(mockSslParameters); + builder.hostnameVerifier(mockHostnameVerifier); + builder.hostAndPortMapper(mockHostAndPortMapper); + + AzureJedisClientConfig config = builder.build(); + + Assertions.assertEquals("fake-user", config.getUser()); + Assertions.assertEquals(1, config.getDatabase()); + Assertions.assertEquals("fake-client-name", config.getClientName()); + Assertions.assertEquals(1500, config.getConnectionTimeoutMillis()); + Assertions.assertEquals(2000, config.getSocketTimeoutMillis()); + Assertions.assertEquals(1400, config.getBlockingSocketTimeoutMillis()); + Assertions.assertEquals(false, config.isSsl()); + + Assertions.assertEquals(mockSslSocketFactory, config.getSslSocketFactory()); + Assertions.assertEquals(mockSslParameters, config.getSslParameters()); + Assertions.assertEquals(mockHostnameVerifier, config.getHostnameVerifier()); + Assertions.assertEquals(mockHostAndPortMapper, config.getHostAndPortMapper()); + + } + + @Test + void testDefaultParams() { + AzureJedisClientConfig config = builder.build(); + + Assertions.assertEquals(null, config.getUser()); + Assertions.assertEquals(Protocol.DEFAULT_DATABASE, config.getDatabase()); + Assertions.assertEquals(null, config.getClientName()); + Assertions.assertEquals(Protocol.DEFAULT_TIMEOUT, config.getConnectionTimeoutMillis()); + Assertions.assertEquals(Protocol.DEFAULT_TIMEOUT, config.getSocketTimeoutMillis()); + Assertions.assertEquals(0, config.getBlockingSocketTimeoutMillis()); + Assertions.assertEquals(false, config.isSsl()); + + + Assertions.assertEquals(null, config.getSslSocketFactory()); + Assertions.assertEquals(null, config.getSslParameters()); + Assertions.assertEquals(null, config.getHostnameVerifier()); + Assertions.assertEquals(null, config.getHostAndPortMapper()); + } + + + @Test + void testGetPasswordFromField() { + builder.password("fake-password"); + AzureJedisClientConfig config = builder.build(); + + Assertions.assertEquals("fake-password", config.getPassword()); + } + + @Test + void testGetPasswordFromCredentialSupplier() { + builder.credentialSupplier(()-> "password-from-credential-supplier"); + AzureJedisClientConfig config = builder.build(); + + Assertions.assertEquals("password-from-credential-supplier", config.getPassword()); + } + + @Test + void testGetPasswordFromCredentialSupplierReturnNull() { + AzureJedisClientConfig config = builder.build(); + + Assertions.assertEquals(null, config.getPassword()); + } + + @Test + void testUpdatePassword() { + builder.credentialSupplier(()-> "password-from-credential-supplier"); + AzureJedisClientConfig config = builder.build(); + + Assertions.assertEquals("password-from-credential-supplier", config.getPassword()); + + config.updatePassword("updated-password"); + Assertions.assertNotEquals("password-from-credential-supplier", config.getPassword()); + Assertions.assertEquals("updated-password", config.getPassword()); + } + +} diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactoryTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactoryTest.java new file mode 100644 index 000000000000..ba4962e677df --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactoryTest.java @@ -0,0 +1,117 @@ +package com.azure.spring.cloud.service.implementation.redis; + + +import org.apache.commons.pool2.PooledObject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class AzureJedisFactoryTest { + + @Test + void testActivateObjectJedis() { + + JedisClientConfig jedisClientConfig = mock(JedisClientConfig.class); + PooledObject pooledJedis = mock(PooledObject.class); + ; + HostAndPort hostAndPort = mock(HostAndPort.class); + Jedis jedis = mock(Jedis.class); + + when(pooledJedis.getObject()).thenReturn(jedis); + when(jedis.getDB()).thenReturn(7); + when(jedis.select(7)).thenReturn("7 selected"); + when(jedisClientConfig.getDatabase()).thenReturn(7); + + AzureJedisFactory testTarget = new AzureJedisFactory(hostAndPort, jedisClientConfig); + testTarget.activateObject(pooledJedis); + + verify(jedis, times(1)).getDB(); + verify(jedis, times(0)).select(7); + verify(jedisClientConfig, times(1)).getDatabase(); + } + + @Test + void testDestroyObject() { + JedisClientConfig jedisClientConfig = mock(JedisClientConfig.class); + PooledObject pooledJedis = mock(PooledObject.class); + ; + HostAndPort hostAndPort = mock(HostAndPort.class); + Jedis jedis = mock(Jedis.class); + + when(pooledJedis.getObject()).thenReturn(jedis); + when(jedis.isConnected()).thenReturn(true); + + AzureJedisFactory testTarget = new AzureJedisFactory(hostAndPort, jedisClientConfig); + testTarget.destroyObject(pooledJedis); + + verify(jedis, times(1)).isConnected(); + verify(jedis, times(1)).close(); + + } + + @Test + void testValidateObjectFalse() { + JedisClientConfig jedisClientConfig = mock(JedisClientConfig.class); + PooledObject pooledJedis = mock(PooledObject.class); + HostAndPort hostAndPort = mock(HostAndPort.class); + Jedis jedis = mock(Jedis.class); + + when(pooledJedis.getObject()).thenReturn(jedis); + when(jedis.isConnected()).thenReturn(true); + + AzureJedisFactory testTarget = new AzureJedisFactory(hostAndPort, jedisClientConfig); + boolean result = testTarget.validateObject(pooledJedis); + + Assertions.assertFalse(result); + } + + @Test + void testValidateObjectTrue() { + JedisClientConfig jedisClientConfig = mock(JedisClientConfig.class); + PooledObject pooledJedis = mock(PooledObject.class); + ; + HostAndPort hostAndPort = mock(HostAndPort.class); + when(hostAndPort.getHost()).thenReturn("mock-host"); + when(hostAndPort.getPort()).thenReturn(1233); + + Jedis jedis = mock(Jedis.class, Mockito.RETURNS_DEEP_STUBS); + when(pooledJedis.getObject()).thenReturn(jedis); + when(jedis.getClient().getHost()).thenReturn("mock-host"); + when(jedis.getClient().getPort()).thenReturn(1233); + when(jedis.isConnected()).thenReturn(true); + when(jedis.ping()).thenReturn("PONG"); + + AzureJedisFactory testTarget = new AzureJedisFactory(hostAndPort, jedisClientConfig); + boolean result = testTarget.validateObject(pooledJedis); + Assertions.assertTrue(result); + + } + + @Test + void testMakeObject() { + JedisClientConfig jedisClientConfig = mock(JedisClientConfig.class); + HostAndPort hostAndPort = mock(HostAndPort.class); + AzureJedisFactory testTarget = new AzureJedisFactory(hostAndPort, jedisClientConfig); + + try (MockedConstruction ignoredVariable = mockConstruction(Jedis.class, + (jedisMocker, context) -> { + when(jedisMocker.isConnected()).thenReturn(true); + when(jedisMocker.get("fake-key")).thenReturn("fake-value"); + + Assertions.assertNotNull(testTarget.makeObject()); + Assertions.assertEquals(jedisMocker, testTarget.makeObject().getObject()); + Assertions.assertEquals("fake-value", testTarget.makeObject().getObject().get("fake-key")); + })) { + } + } +} From 3b47ea6e8ce9f374bf56b18f4ef3a06099befe84 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 10:10:43 +0800 Subject: [PATCH 06/38] update tests --- .../AzureJedisConnectionFactoryTest.java | 163 +++++++++++++++--- ...edisPasswordlessAutoConfigurationTest.java | 40 ++++- .../AzureRedisCredentialSupplierTest.java | 4 +- .../AzureRedisPasswordlessProperties.java | 10 +- .../redis/AzureJedisClientConfig.java | 2 +- .../redis/AzureJedisClientConfigTest.java | 8 +- .../redis/AzureJedisFactoryTest.java | 14 +- 7 files changed, 191 insertions(+), 50 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java index d116be5a6e6c..5b1ac1f52d06 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java @@ -1,13 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; import com.azure.spring.cloud.core.implementation.util.ReflectionUtils; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisPassword; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; +import org.springframework.data.redis.connection.jedis.JedisConnection; import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.util.Pool; import java.time.Duration; @@ -15,6 +23,7 @@ import java.util.function.Supplier; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.when; class AzureJedisConnectionFactoryTest { @@ -24,37 +33,135 @@ class AzureJedisConnectionFactoryTest { Supplier credentialSupplier; @Test - void getConnectionWithoutInit() { + void testPostProcessConnection() { AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertThrows(IllegalStateException.class, () -> { - azureJedisConnectionFactory.getConnection(); - }); + JedisConnection mockJedisConnection = mock(JedisConnection.class); + + JedisConnection jedisConnection = azureJedisConnectionFactory.postProcessConnection(mockJedisConnection); + + Assertions.assertEquals(mockJedisConnection, jedisConnection); + } - // @Test - void getConnection() { + void testFetchJedisConnectorWithNoPool() { AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - azureJedisConnectionFactory.getConnection(); + + try (MockedConstruction jedisMockedConstruction = mockConstruction(Jedis.class, + (jedisMocker, context) -> { + when(jedisMocker.isConnected()).thenReturn(true); + when(jedisMocker.get("fake-key")).thenReturn("fake-value"); + + Jedis jedis = azureJedisConnectionFactory.fetchJedisConnector(); + + Assertions.assertEquals(jedisMocker, jedis); + })) { + Assertions.assertEquals(0, jedisMockedConstruction.constructed().size()); + } } @Test - void afterPropertiesSet() { + @SuppressWarnings("unchecked") + void testFetchJedisConnectorWithPool() { + clientConfiguration = mock(JedisClientConfiguration.class); + when(clientConfiguration.isUsePooling()).thenReturn(true); + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Jedis mockJedis = mock(Jedis.class); + Pool mockPool = mock(Pool.class); + when(mockPool.getResource()).thenReturn(mockJedis); + azureJedisConnectionFactory.setPool(mockPool); + + Jedis jedis = azureJedisConnectionFactory.fetchJedisConnector(); + + Assertions.assertEquals(mockJedis, jedis); + } @Test - void postProcessConnection() { + void testGetConnection() { + standaloneConfig = mock(RedisStandaloneConfiguration.class); + when(standaloneConfig.getUsername()).thenReturn("fake-userName"); + when(standaloneConfig.getPassword()).thenReturn(RedisPassword.none()); + + clientConfiguration = mock(JedisClientConfiguration.class); + when(clientConfiguration.getClientName()).thenReturn(Optional.of("fake-clientName")); + when(clientConfiguration.isUsePooling()).thenReturn(false); + + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + + try (MockedConstruction jedisMockedConstruction = mockConstruction(Jedis.class, + (jedisMocker, context) -> { + + RedisConnection connection = azureJedisConnectionFactory.getConnection(); + + Assertions.assertNotNull(connection); + Assertions.assertEquals("fake-clientName", connection.getClientName()); + + })) { + Assertions.assertEquals(0, jedisMockedConstruction.constructed().size()); + } } @Test - void createRedisPool() { + void testGetConnectionWithoutInit() { + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Assertions.assertThrows(IllegalStateException.class, () -> { + azureJedisConnectionFactory.getConnection(); + }); + } + + @Test + @SuppressWarnings("unchecked") + void testAfterPropertiesSet() { + standaloneConfig = mock(RedisStandaloneConfiguration.class); + when(standaloneConfig.getUsername()).thenReturn("fake-userName"); + when(standaloneConfig.getPassword()).thenReturn(RedisPassword.none()); + + clientConfiguration = mock(JedisClientConfiguration.class); + when(clientConfiguration.getClientName()).thenReturn(Optional.of("fake-clientName")); + when(clientConfiguration.isUsePooling()).thenReturn(true); + when(clientConfiguration.getPoolConfig()).thenReturn(Optional.of(new JedisPoolConfig())); + + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + + JedisClientConfig jedisClientConfig = (JedisClientConfig) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "jedisClientConfig", azureJedisConnectionFactory); + Boolean initialized = (Boolean) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "initialized", azureJedisConnectionFactory); + Pool pool = (Pool) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "pool", azureJedisConnectionFactory); + + Assertions.assertNull(jedisClientConfig); + Assertions.assertFalse(initialized); + Assertions.assertNull(pool); + + azureJedisConnectionFactory.afterPropertiesSet(); + + jedisClientConfig = (JedisClientConfig) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "jedisClientConfig", azureJedisConnectionFactory); + initialized = (Boolean) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "initialized", azureJedisConnectionFactory); + pool = (Pool) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "pool", azureJedisConnectionFactory); + Assertions.assertNotNull(jedisClientConfig); + Assertions.assertTrue(initialized); + Assertions.assertNotNull(pool); + } @Test - void fetchJedisConnector() { + void testCreateRedisPool() { + standaloneConfig = mock(RedisStandaloneConfiguration.class); + when(standaloneConfig.getUsername()).thenReturn("fake-userName"); + when(standaloneConfig.getHostName()).thenReturn("fake-hostName"); + when(standaloneConfig.getPort()).thenReturn(1233); + + clientConfiguration = mock(JedisClientConfiguration.class); + when(clientConfiguration.getPoolConfig()).thenReturn(Optional.of(new JedisPoolConfig())); + + AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); + Pool redisPool = azureJedisConnectionFactory.createRedisPool(); + + Assertions.assertNotNull(redisPool); + } @Test + @SuppressWarnings("unchecked") void testDestroyWithPool() { clientConfiguration = mock(JedisClientConfiguration.class); @@ -80,7 +187,7 @@ void testDestroyWithPool() { } @Test - void translateExceptionIfPossible() { + void testTranslateExceptionIfPossible() { AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); Assertions.assertThrows(UnsupportedOperationException.class, () -> { azureJedisConnectionFactory.translateExceptionIfPossible(new RuntimeException()); @@ -88,9 +195,8 @@ void translateExceptionIfPossible() { } - @Test - void getClusterConnection() { + void testGetClusterConnection() { AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); @@ -100,7 +206,7 @@ void getClusterConnection() { } @Test - void getPoolConfig() { + void testGetPoolConfig() { clientConfiguration = mock(JedisClientConfiguration.class); GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig<>(); when(clientConfiguration.getPoolConfig()).thenReturn(Optional.of(poolConfig)); @@ -110,13 +216,13 @@ void getPoolConfig() { } @Test - void getConvertPipelineAndTxResults() { + void testGetConvertPipelineAndTxResults() { AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); Assertions.assertTrue(azureJedisConnectionFactory.getConvertPipelineAndTxResults()); } @Test - void getSentinelConnection() { + void testGetSentinelConnection() { AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); Assertions.assertThrows(UnsupportedOperationException.class, () -> { azureJedisConnectionFactory.getSentinelConnection(); @@ -124,7 +230,7 @@ void getSentinelConnection() { } @Test - void getUsePool() { + void testGetUsePool() { clientConfiguration = mock(JedisClientConfiguration.class); when(clientConfiguration.isUsePooling()).thenReturn(true); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); @@ -137,7 +243,7 @@ void getUsePool() { } @Test - void getDatabase() { + void testGetDatabase() { standaloneConfig = mock(RedisStandaloneConfiguration.class); when(standaloneConfig.getDatabase()).thenReturn(7); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); @@ -145,7 +251,7 @@ void getDatabase() { } @Test - void getClientConfiguration() { + void testGetClientConfiguration() { AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); JedisClientConfiguration configuration = azureJedisConnectionFactory.getClientConfiguration(); @@ -158,7 +264,7 @@ void getClientConfiguration() { } @Test - void setConvertPipelineAndTxResults() { + void testSetConvertPipelineAndTxResults() { AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); azureJedisConnectionFactory.setConvertPipelineAndTxResults(false); @@ -169,7 +275,7 @@ void setConvertPipelineAndTxResults() { } @Test - void getPort() { + void testGetPort() { standaloneConfig = mock(RedisStandaloneConfiguration.class); when(standaloneConfig.getPort()).thenReturn(1717); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); @@ -177,7 +283,7 @@ void getPort() { } @Test - void isUseSsl() { + void testIsUseSsl() { clientConfiguration = mock(JedisClientConfiguration.class); when(clientConfiguration.isUseSsl()).thenReturn(true); @@ -190,7 +296,7 @@ void isUseSsl() { } @Test - void getHostName() { + void testGetHostName() { standaloneConfig = mock(RedisStandaloneConfiguration.class); when(standaloneConfig.getHostName()).thenReturn("fake-host-name"); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); @@ -198,7 +304,7 @@ void getHostName() { } @Test - void getPasswordFromConfig() { + void testGetPasswordFromConfig() { standaloneConfig = mock(RedisStandaloneConfiguration.class); when(standaloneConfig.getPassword()).thenReturn(RedisPassword.of("password-from-config")); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); @@ -206,7 +312,8 @@ void getPasswordFromConfig() { } @Test - void getPasswordFromSupplier() { + @SuppressWarnings("unchecked") + void testGetPasswordFromSupplier() { standaloneConfig = mock(RedisStandaloneConfiguration.class); credentialSupplier = mock(Supplier.class); when(credentialSupplier.get()).thenReturn("password-from-credential-supplier"); @@ -215,7 +322,7 @@ void getPasswordFromSupplier() { } @Test - void getClientName() { + void testGetClientName() { clientConfiguration = mock(JedisClientConfiguration.class); when(clientConfiguration.getClientName()).thenReturn(Optional.of("fake-clientName")); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); @@ -223,7 +330,7 @@ void getClientName() { } @Test - void getTimeout() { + void testGetTimeout() { clientConfiguration = mock(JedisClientConfiguration.class); when(clientConfiguration.getReadTimeout()).thenReturn(Duration.ofSeconds(23)); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java index 25584a3c8382..246e0d86fba9 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -3,6 +3,8 @@ package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -14,6 +16,7 @@ import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import java.time.Duration; +import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; @@ -21,6 +24,12 @@ * Tests for {@link AzureJedisPasswordlessAutoConfiguration} when Lettuce is not on the classpath. */ class AzureJedisPasswordlessAutoConfigurationTest { + private static final String AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME = "azureRedisCredentialSupplier"; + + private static final String REDIS_SCOPE_GLOBAL = "https://*.cacheinfra.windows.net:10225/appid/.default"; + private static final String REDIS_SCOPE_CHINA = "https://*.cacheinfra.windows.net.china:10225/appid/.default"; + private static final String REDIS_SCOPE_GERMANY = "https://*.cacheinfra.windows.net.germany:10225/appid/.default"; + private static final String REDIS_SCOPE_US_GOVERNMENT = "https://*.cacheinfra.windows.us.government.net:10225/appid/.default"; private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withPropertyValues( @@ -31,17 +40,36 @@ class AzureJedisPasswordlessAutoConfigurationTest { .withConfiguration(AutoConfigurations.of(AzureJedisPasswordlessAutoConfiguration.class)); @Test - void testScopes() { - + @SuppressWarnings("unchecked") + void testCredentialSupplier() { + this.contextRunner.run((context) -> { + Supplier supplier = (Supplier) context.getBean(AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME); + Assertions.assertNotNull(supplier); + }); } @Test - void testCredentialSupplier() { + void testScopes() { - } + this.contextRunner.run((context) -> { + AzureRedisPasswordlessProperties properties = context.getBean(AzureRedisPasswordlessProperties.class); + Assertions.assertEquals(REDIS_SCOPE_GLOBAL, properties.getScopes()); + }); - @Test - void testProperties() { + this.contextRunner.withPropertyValues("spring.redis.azure.profile.cloud-type = AZURE_CHINA").run((context) -> { + AzureRedisPasswordlessProperties properties = context.getBean(AzureRedisPasswordlessProperties.class); + Assertions.assertEquals(REDIS_SCOPE_CHINA, properties.getScopes()); + }); + + this.contextRunner.withPropertyValues("spring.redis.azure.profile.cloud-type = AZURE_GERMANY").run((context) -> { + AzureRedisPasswordlessProperties properties = context.getBean(AzureRedisPasswordlessProperties.class); + Assertions.assertEquals(REDIS_SCOPE_GERMANY, properties.getScopes()); + }); + + this.contextRunner.withPropertyValues("spring.redis.azure.profile.cloud-type = AZURE_US_GOVERNMENT").run((context) -> { + AzureRedisPasswordlessProperties properties = context.getBean(AzureRedisPasswordlessProperties.class); + Assertions.assertEquals(REDIS_SCOPE_US_GOVERNMENT, properties.getScopes()); + }); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java index d5f6c89ce75c..3fb8de30415d 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java @@ -1,5 +1,7 @@ -package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; import org.junit.jupiter.api.Assertions; diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java index be82b243d07c..3536bdfb61a9 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java @@ -13,17 +13,15 @@ */ public class AzureRedisPasswordlessProperties extends AzurePasswordlessProperties { - // todo private static final Map REDIS_SCOPE_MAP = new HashMap() { { put(AzureProfileOptionsProvider.CloudType.AZURE, "https://*.cacheinfra.windows.net:10225/appid/.default"); - put(AzureProfileOptionsProvider.CloudType.AZURE_CHINA, "https://*.cacheinfra.windows.net:10225/appid/.default"); - put(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY, "https://*.cacheinfra.windows.net:10225/appid/.default"); - put(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT, "https://*.cacheinfra.windows.net:10225/appid/.default"); + put(AzureProfileOptionsProvider.CloudType.AZURE_CHINA, "https://*.cacheinfra.windows.net.china:10225/appid/.default"); + put(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY, "https://*.cacheinfra.windows.net.germany:10225/appid/.default"); + put(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT, "https://*.cacheinfra.windows.us.government.net:10225/appid/.default"); } }; - @Override public String getScopes() { if (super.getScopes() == null) { @@ -42,7 +40,7 @@ private String getRedisScopes() { } else if (AzureProfileOptionsProvider.CloudType.AZURE_GERMANY.equals(cloudType)) { redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY); } else if (AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT.equals(cloudType)) { - redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY); + redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT); } return redisScope; } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java index 3a2f1acf66a0..86683be7818a 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfig.java @@ -34,7 +34,7 @@ public final class AzureJedisClientConfig implements JedisClientConfig { private final HostAndPortMapper hostAndPortMapper; - private Supplier credentialSupplier ; + private Supplier credentialSupplier; private AzureJedisClientConfig(int connectionTimeoutMillis, int soTimeoutMillis, int blockingSocketTimeoutMillis, String user, String password, int database, String clientName, diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfigTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfigTest.java index 04c8757fd6f4..90f86b303857 100644 --- a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfigTest.java +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisClientConfigTest.java @@ -1,5 +1,7 @@ -package com.azure.spring.cloud.service.implementation.redis; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.service.implementation.redis; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -82,7 +84,7 @@ void testGetPasswordFromField() { @Test void testGetPasswordFromCredentialSupplier() { - builder.credentialSupplier(()-> "password-from-credential-supplier"); + builder.credentialSupplier(() -> "password-from-credential-supplier"); AzureJedisClientConfig config = builder.build(); Assertions.assertEquals("password-from-credential-supplier", config.getPassword()); @@ -97,7 +99,7 @@ void testGetPasswordFromCredentialSupplierReturnNull() { @Test void testUpdatePassword() { - builder.credentialSupplier(()-> "password-from-credential-supplier"); + builder.credentialSupplier(() -> "password-from-credential-supplier"); AzureJedisClientConfig config = builder.build(); Assertions.assertEquals("password-from-credential-supplier", config.getPassword()); diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactoryTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactoryTest.java index ba4962e677df..2c766c1830ce 100644 --- a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactoryTest.java +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/redis/AzureJedisFactoryTest.java @@ -1,5 +1,7 @@ -package com.azure.spring.cloud.service.implementation.redis; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.service.implementation.redis; import org.apache.commons.pool2.PooledObject; import org.junit.jupiter.api.Assertions; @@ -19,11 +21,11 @@ class AzureJedisFactoryTest { @Test + @SuppressWarnings("unchecked") void testActivateObjectJedis() { JedisClientConfig jedisClientConfig = mock(JedisClientConfig.class); PooledObject pooledJedis = mock(PooledObject.class); - ; HostAndPort hostAndPort = mock(HostAndPort.class); Jedis jedis = mock(Jedis.class); @@ -41,10 +43,10 @@ void testActivateObjectJedis() { } @Test + @SuppressWarnings("unchecked") void testDestroyObject() { JedisClientConfig jedisClientConfig = mock(JedisClientConfig.class); PooledObject pooledJedis = mock(PooledObject.class); - ; HostAndPort hostAndPort = mock(HostAndPort.class); Jedis jedis = mock(Jedis.class); @@ -60,6 +62,7 @@ void testDestroyObject() { } @Test + @SuppressWarnings("unchecked") void testValidateObjectFalse() { JedisClientConfig jedisClientConfig = mock(JedisClientConfig.class); PooledObject pooledJedis = mock(PooledObject.class); @@ -76,10 +79,10 @@ void testValidateObjectFalse() { } @Test + @SuppressWarnings("unchecked") void testValidateObjectTrue() { JedisClientConfig jedisClientConfig = mock(JedisClientConfig.class); PooledObject pooledJedis = mock(PooledObject.class); - ; HostAndPort hostAndPort = mock(HostAndPort.class); when(hostAndPort.getHost()).thenReturn("mock-host"); when(hostAndPort.getPort()).thenReturn(1233); @@ -103,7 +106,7 @@ void testMakeObject() { HostAndPort hostAndPort = mock(HostAndPort.class); AzureJedisFactory testTarget = new AzureJedisFactory(hostAndPort, jedisClientConfig); - try (MockedConstruction ignoredVariable = mockConstruction(Jedis.class, + try (MockedConstruction jedisMockedConstruction = mockConstruction(Jedis.class, (jedisMocker, context) -> { when(jedisMocker.isConnected()).thenReturn(true); when(jedisMocker.get("fake-key")).thenReturn("fake-value"); @@ -112,6 +115,7 @@ void testMakeObject() { Assertions.assertEquals(jedisMocker, testTarget.makeObject().getObject()); Assertions.assertEquals("fake-value", testTarget.makeObject().getObject().get("fake-key")); })) { + Assertions.assertEquals(0, jedisMockedConstruction.constructed().size()); } } } From ce8f20dbca91e41a5cce3d0e5c65874818df2c41 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 11:00:15 +0800 Subject: [PATCH 07/38] fix-pipeline-errors --- sdk/spring/spring-cloud-azure-service/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-service/pom.xml b/sdk/spring/spring-cloud-azure-service/pom.xml index cd192934fa30..85d74815070a 100644 --- a/sdk/spring/spring-cloud-azure-service/pom.xml +++ b/sdk/spring/spring-cloud-azure-service/pom.xml @@ -162,7 +162,7 @@ org.apache.kafka:kafka-clients:[3.1.2] - redis.clients:jedis:[3.8.0] + redis.clients:jedis:[3.8.0] From 9f763d866fded9539ac12bea92edfac3cf35f285 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 11:20:19 +0800 Subject: [PATCH 08/38] fix pipeline errors --- sdk/spring/spring-cloud-azure-autoconfigure/pom.xml | 2 +- sdk/spring/spring-cloud-azure-starter-redis/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml b/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml index 428ad4a19736..3ad533de66d4 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml +++ b/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml @@ -282,7 +282,7 @@ com.azure azure-identity-extensions - 1.1.0-beta.1 + 1.0.0 true diff --git a/sdk/spring/spring-cloud-azure-starter-redis/pom.xml b/sdk/spring/spring-cloud-azure-starter-redis/pom.xml index db219e1240f6..1d6f718dd4e4 100644 --- a/sdk/spring/spring-cloud-azure-starter-redis/pom.xml +++ b/sdk/spring/spring-cloud-azure-starter-redis/pom.xml @@ -106,7 +106,7 @@ com.azure azure-identity-extensions - 1.1.0-beta.1 + 1.0.0 From 04827b56e0dd76afa5b82dc9b04f5b48ba304169 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 11:30:14 +0800 Subject: [PATCH 09/38] fix pipeline errors --- sdk/spring/spring-cloud-azure-autoconfigure/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml b/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml index 3ad533de66d4..8cf67dfdc2e6 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml +++ b/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml @@ -384,6 +384,7 @@ org.springframework.security:spring-security-config:[5.7.6] org.springframework:spring-jms:[5.3.24] org.springframework:spring-jdbc:[5.3.24] + redis.clients:jedis:[3.8.0] From 09a1fc897fa74001269824ee8038c3524ea10b16 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 11:51:05 +0800 Subject: [PATCH 10/38] fix pipeline errors --- .../jedis/AzureJedisConnectionFactory.java | 4 +-- .../AzureJedisConnectionFactoryTest.java | 29 ------------------- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java index 0091e76096b2..a9fd43ccfef6 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -229,12 +229,12 @@ public int getTimeout() { return getReadTimeout(); } - protected JedisConnection postProcessConnection(JedisConnection connection) { + private JedisConnection postProcessConnection(JedisConnection connection) { return connection; } @SuppressWarnings("unchecked") - protected Pool createRedisPool() { + private Pool createRedisPool() { return new AzureJedisPool(this.clientConfiguration.getPoolConfig().get(), new HostAndPort(this.standaloneConfig.getHostName(), this.standaloneConfig.getPort()), this.jedisClientConfig); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java index 5b1ac1f52d06..fcd31a8a1f05 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java @@ -12,7 +12,6 @@ import org.springframework.data.redis.connection.RedisPassword; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; -import org.springframework.data.redis.connection.jedis.JedisConnection; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.JedisPoolConfig; @@ -32,17 +31,6 @@ class AzureJedisConnectionFactoryTest { JedisClientConfiguration clientConfiguration; Supplier credentialSupplier; - @Test - void testPostProcessConnection() { - AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - JedisConnection mockJedisConnection = mock(JedisConnection.class); - - JedisConnection jedisConnection = azureJedisConnectionFactory.postProcessConnection(mockJedisConnection); - - Assertions.assertEquals(mockJedisConnection, jedisConnection); - - } - @Test void testFetchJedisConnectorWithNoPool() { AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); @@ -143,23 +131,6 @@ void testAfterPropertiesSet() { } - @Test - void testCreateRedisPool() { - standaloneConfig = mock(RedisStandaloneConfiguration.class); - when(standaloneConfig.getUsername()).thenReturn("fake-userName"); - when(standaloneConfig.getHostName()).thenReturn("fake-hostName"); - when(standaloneConfig.getPort()).thenReturn(1233); - - clientConfiguration = mock(JedisClientConfiguration.class); - when(clientConfiguration.getPoolConfig()).thenReturn(Optional.of(new JedisPoolConfig())); - - AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Pool redisPool = azureJedisConnectionFactory.createRedisPool(); - - Assertions.assertNotNull(redisPool); - - } - @Test @SuppressWarnings("unchecked") void testDestroyWithPool() { From 4b4815176bf956d891f4b87bd606b1204d09384b Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 12:29:19 +0800 Subject: [PATCH 11/38] fix pipeline errors --- .../jedis/AzureJedisConnectionFactory.java | 6 +++-- ...edisPasswordlessAutoConfigurationTest.java | 22 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java index a9fd43ccfef6..a952f081dde7 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -186,10 +186,12 @@ public String getHostName() { @Nullable public String getPassword() { RedisPassword password = getRedisPassword(); - if (password == null & credentialSupplier != null) { + if (password.isPresent()){ + return password.map(String::new).orElse(null); + }else if (credentialSupplier != null){ return credentialSupplier.get(); } - return password.map(String::new).orElse(null); + return null; } /** diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java index 246e0d86fba9..d1782b6bb788 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -19,6 +19,8 @@ import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Tests for {@link AzureJedisPasswordlessAutoConfiguration} when Lettuce is not on the classpath. @@ -80,14 +82,18 @@ void connectionFactoryDefaultsToJedis() { } @Test - void testOverrideRedisConfiguration() { - this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1").run((context) -> { - AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getDatabase()).isEqualTo(1); - assertThat(cf.getPassword()).isNull(); - assertThat(cf.isUseSsl()).isTrue(); - }); + void testGetPasswordFromSupplier() { + Supplier mockCredentialSupplier = mock(Supplier.class); + when(mockCredentialSupplier.get()).thenReturn("fake-password-from-mock-supplier"); + this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1") + .withBean("azureRedisCredentialSupplier", Supplier.class, ()-> mockCredentialSupplier, null) + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.getHostName()).isEqualTo("foo"); + assertThat(cf.getDatabase()).isEqualTo(1); + assertThat(cf.getPassword()).isEqualTo("fake-password-from-mock-supplier"); + assertThat(cf.isUseSsl()).isTrue(); + }); } @Test From 43e8d293ebab87a6336c394c4f8af313d78ddaa1 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 13:10:51 +0800 Subject: [PATCH 12/38] fix-pipeline-errors --- .../redis/passwordless/jedis/AzureJedisConnectionFactory.java | 4 ++-- .../jedis/AzureJedisPasswordlessAutoConfigurationTest.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java index a952f081dde7..5a32025411ac 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -186,9 +186,9 @@ public String getHostName() { @Nullable public String getPassword() { RedisPassword password = getRedisPassword(); - if (password.isPresent()){ + if (password != null && password.isPresent()) { return password.map(String::new).orElse(null); - }else if (credentialSupplier != null){ + } else if (credentialSupplier != null) { return credentialSupplier.get(); } return null; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java index d1782b6bb788..950168624a4d 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -82,11 +82,12 @@ void connectionFactoryDefaultsToJedis() { } @Test + @SuppressWarnings("unchecked") void testGetPasswordFromSupplier() { Supplier mockCredentialSupplier = mock(Supplier.class); when(mockCredentialSupplier.get()).thenReturn("fake-password-from-mock-supplier"); this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1") - .withBean("azureRedisCredentialSupplier", Supplier.class, ()-> mockCredentialSupplier, null) + .withBean("azureRedisCredentialSupplier", Supplier.class, () -> mockCredentialSupplier, beanDefinition -> beanDefinition.setPrimary(true)) .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); assertThat(cf.getHostName()).isEqualTo("foo"); From b6d43000284ab8a7441a7502ac5a824bd5fe775d Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 13:44:12 +0800 Subject: [PATCH 13/38] fix-code-review --- sdk/spring/ci.yml | 2 +- sdk/spring/spring-cloud-azure-starter-redis/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/spring/ci.yml b/sdk/spring/ci.yml index 875bdcd397ee..39daef15d9a7 100644 --- a/sdk/spring/ci.yml +++ b/sdk/spring/ci.yml @@ -517,7 +517,7 @@ extends: skipUpdatePackageJson: true skipVerifyChangelog: true releaseInBatch: ${{ parameters.release_springcloudazurestarterjdbcpostgresql }} - - name: spring-cloud-azure-starter-jdbc-postgresql + - name: spring-cloud-azure-starter-redis groupId: com.azure.spring safeName: springcloudazurestarterredis skipPublishDocGithubIo: true diff --git a/sdk/spring/spring-cloud-azure-starter-redis/pom.xml b/sdk/spring/spring-cloud-azure-starter-redis/pom.xml index 1d6f718dd4e4..9f553826adfa 100644 --- a/sdk/spring/spring-cloud-azure-starter-redis/pom.xml +++ b/sdk/spring/spring-cloud-azure-starter-redis/pom.xml @@ -100,7 +100,7 @@ redis.clients jedis - 3.8.0 + 3.8.0 From ad72e38c91827dc36f99076cdd84c2f41649cd46 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 13:49:12 +0800 Subject: [PATCH 14/38] fix-code-review --- .../checkstyle/checkstyle-suppressions.xml | 2 +- eng/versioning/external_dependencies.txt | 2 +- .../jedis/AzureJedisConnectionFactory.java | 10 +-- .../AzureJedisConnectionFactoryTest.java | 4 +- ...edisPasswordlessAutoConfigurationTest.java | 2 +- .../AzurePasswordlessProperties.java | 5 +- .../AzureRedisPasswordlessProperties.java | 5 +- .../AzureRedisPasswordlessPropertiesTest.java | 82 +++++++++++++++++++ 8 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java diff --git a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml index 658470eb9be5..071d5ce7f731 100755 --- a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml +++ b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml @@ -434,7 +434,7 @@ the main ServiceBusClientBuilder. --> - + diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index 778d3d0ab3b0..0893e5895dca 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -399,5 +399,5 @@ storage_com.microsoft.azure:azure-storage;8.4.0 # sdk\appconfiguration\azure-spring-cloud-test-appconfiguration-config\pom.xml spring_com.microsoft.azure:azure;1.34.0 -# sdk/spring/spring-cloud-azure-service/pom.xml +# sdk\spring\spring-cloud-azure-service\pom.xml spring_redis.clients:jedis;3.8.0 \ No newline at end of file diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java index 5a32025411ac..b46b1a28d177 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -54,9 +54,9 @@ public class AzureJedisConnectionFactory implements InitializingBean, Disposable /** * Constructs a new AzureJedisConnectionFactory instance using the given RedisStandaloneConfiguration, JedisClientConfiguration and CredentialSupplier. * - * @param standaloneConfig must not be {@literal null}. + * @param standaloneConfig must not be {@literal null}. * @param clientConfiguration must not be {@literal null}. - * @param credentialSupplier must not be {@literal null}. + * @param credentialSupplier must not be {@literal null}. */ public AzureJedisConnectionFactory(RedisStandaloneConfiguration standaloneConfig, JedisClientConfiguration clientConfiguration, Supplier credentialSupplier) { this.standaloneConfig = standaloneConfig; @@ -138,6 +138,7 @@ public void setConvertPipelineAndTxResults(boolean convertPipelineAndTxResults) * Indicates the use of a connection pool. *

* Applies only to single node Redis. + * * @return the use of connection pooling. */ public boolean getUsePool() { @@ -186,7 +187,7 @@ public String getHostName() { @Nullable public String getPassword() { RedisPassword password = getRedisPassword(); - if (password != null && password.isPresent()) { + if (password.isPresent()) { return password.map(String::new).orElse(null); } else if (credentialSupplier != null) { return credentialSupplier.get(); @@ -242,13 +243,12 @@ private Pool createRedisPool() { this.jedisClientConfig); } - /** * Returns a Jedis instance to be used as a Redis connection. The instance can be newly created or retrieved from a * pool. * - * @throws RedisConnectionFailureException when can't fetch a jedis instance. * @return Jedis instance ready for wrapping into a {@link RedisConnection}. + * @throws RedisConnectionFailureException when can't fetch a jedis instance. */ protected Jedis fetchJedisConnector() { try { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java index fcd31a8a1f05..680b76a32e4f 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java @@ -21,6 +21,7 @@ import java.util.Optional; import java.util.function.Supplier; +import static org.mockito.Answers.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.when; @@ -285,7 +286,8 @@ void testGetPasswordFromConfig() { @Test @SuppressWarnings("unchecked") void testGetPasswordFromSupplier() { - standaloneConfig = mock(RedisStandaloneConfiguration.class); + standaloneConfig = mock(RedisStandaloneConfiguration.class, RETURNS_DEEP_STUBS); + when(standaloneConfig.getPassword().isPresent()).thenReturn(false); credentialSupplier = mock(Supplier.class); when(credentialSupplier.get()).thenReturn("password-from-credential-supplier"); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java index 950168624a4d..f453bce26c54 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -23,7 +23,7 @@ import static org.mockito.Mockito.when; /** - * Tests for {@link AzureJedisPasswordlessAutoConfiguration} when Lettuce is not on the classpath. + * Tests for {@link AzureJedisPasswordlessAutoConfiguration}. */ class AzureJedisPasswordlessAutoConfigurationTest { private static final String AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME = "azureRedisCredentialSupplier"; diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java index b1ed0faf8a55..a15030d88c05 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java @@ -126,7 +126,10 @@ private enum AzurePasswordlessPropertiesMapping { (p, s) -> p.setProperty(AuthProperty.USERNAME.getPropertyKey(), s)), tenantId(p -> p.getProfile().getTenantId(), - (p, s) -> p.setProperty(AuthProperty.TENANT_ID.getPropertyKey(), s)); + (p, s) -> p.setProperty(AuthProperty.TENANT_ID.getPropertyKey(), s)), + + authorityHost(p -> p.getProfile().getEnvironment().getActiveDirectoryEndpoint(), + (p, s) -> p.setProperty(AuthProperty.AUTHORITY_HOST.getPropertyKey(), s)); private Function getter; private BiConsumer setter; diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java index 3536bdfb61a9..5889d8e9da60 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java @@ -24,10 +24,7 @@ public class AzureRedisPasswordlessProperties extends AzurePasswordlessPropertie @Override public String getScopes() { - if (super.getScopes() == null) { - super.setScopes(getRedisScopes()); - } - return super.getScopes(); + return super.getScopes() == null ? getRedisScopes() : super.getScopes(); } private String getRedisScopes() { diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java new file mode 100644 index 000000000000..645f4cb5bac3 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.service.implementation.passwordless; + +import com.azure.identity.extensions.implementation.enums.AuthProperty; +import com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; +import com.azure.spring.cloud.core.properties.profile.AzureProfileProperties; +import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Properties; + +class AzureRedisPasswordlessPropertiesTest { + + private static final String REDIS_SCOPE_GLOBAL = "https://*.cacheinfra.windows.net:10225/appid/.default"; + private static final String REDIS_SCOPE_CHINA = "https://*.cacheinfra.windows.net.china:10225/appid/.default"; + private static final String REDIS_SCOPE_GERMANY = "https://*.cacheinfra.windows.net.germany:10225/appid/.default"; + private static final String REDIS_SCOPE_US_GOVERNMENT = "https://*.cacheinfra.windows.us.government.net:10225/appid/.default"; + + @Test + void testGetScopes() { + AzureProfileProperties profile = new AzureProfileProperties(); + + AzureRedisPasswordlessProperties properties = new AzureRedisPasswordlessProperties(); + String scopes = properties.getScopes(); + Assertions.assertEquals(REDIS_SCOPE_GLOBAL, scopes); + + profile.setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY); + properties.setProfile(profile); + scopes = properties.getScopes(); + Assertions.assertEquals(REDIS_SCOPE_GERMANY, scopes); + + profile.setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_CHINA); + properties.setProfile(profile); + scopes = properties.getScopes(); + Assertions.assertEquals(REDIS_SCOPE_CHINA, scopes); + + profile.setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT); + properties.setProfile(profile); + scopes = properties.getScopes(); + Assertions.assertEquals(REDIS_SCOPE_US_GOVERNMENT, scopes); + + properties.setScopes("fake-scopes"); + scopes = properties.getScopes(); + Assertions.assertEquals("fake-scopes", scopes); + + } + + @Test + void testToProperties() { + TokenCredentialProperties credential = new TokenCredentialProperties(); + credential.setClientSecret("fake-client-secret"); + credential.setClientId("fake-client-id"); + credential.setUsername("fake-username"); + credential.setPassword("fake-password"); + credential.setClientCertificatePath("fake-client-certificate-path"); + credential.setClientCertificatePassword("fake-client-certificate-password"); + + AzureProfileProperties profile = new AzureProfileProperties(); + profile.setTenantId("fake-tenantId"); + profile.setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY); + + AzureRedisPasswordlessProperties azureRedisPasswordlessProperties = new AzureRedisPasswordlessProperties(); + azureRedisPasswordlessProperties.setScopes("fake-scopes"); + azureRedisPasswordlessProperties.setCredential(credential); + azureRedisPasswordlessProperties.setProfile(profile); + + Properties properties = azureRedisPasswordlessProperties.toProperties(); + + + Assertions.assertEquals("fake-client-id", properties.getProperty(AuthProperty.CLIENT_ID.getPropertyKey())); + Assertions.assertEquals("fake-client-secret", properties.getProperty(AuthProperty.CLIENT_SECRET.getPropertyKey())); + Assertions.assertEquals("fake-username", properties.getProperty(AuthProperty.USERNAME.getPropertyKey())); + Assertions.assertEquals("fake-password", properties.getProperty(AuthProperty.PASSWORD.getPropertyKey())); + Assertions.assertEquals("fake-client-certificate-path", properties.getProperty(AuthProperty.CLIENT_CERTIFICATE_PATH.getPropertyKey())); + Assertions.assertEquals("fake-client-certificate-password", properties.getProperty(AuthProperty.CLIENT_CERTIFICATE_PASSWORD.getPropertyKey())); + Assertions.assertEquals("fake-tenantId", properties.getProperty(AuthProperty.TENANT_ID.getPropertyKey())); + Assertions.assertEquals("fake-scopes", properties.getProperty(AuthProperty.SCOPES.getPropertyKey())); + } +} From bbcebc15138cfff426c6782f16feec23c5483e17 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 15:42:05 +0800 Subject: [PATCH 15/38] fix linting-reports --- .../redis/passwordless/jedis/AzureJedisConnectionFactory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java index b46b1a28d177..327ae252198c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -54,9 +54,9 @@ public class AzureJedisConnectionFactory implements InitializingBean, Disposable /** * Constructs a new AzureJedisConnectionFactory instance using the given RedisStandaloneConfiguration, JedisClientConfiguration and CredentialSupplier. * - * @param standaloneConfig must not be {@literal null}. + * @param standaloneConfig must not be {@literal null}. * @param clientConfiguration must not be {@literal null}. - * @param credentialSupplier must not be {@literal null}. + * @param credentialSupplier must not be {@literal null}. */ public AzureJedisConnectionFactory(RedisStandaloneConfiguration standaloneConfig, JedisClientConfiguration clientConfiguration, Supplier credentialSupplier) { this.standaloneConfig = standaloneConfig; From deeae0b7ce08fc1aa3df5b8d38e9ee3680d3b132 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 9 Jan 2023 16:25:30 +0800 Subject: [PATCH 16/38] update CHANGELOG.md --- sdk/spring/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index d8a033c56ede..86e1c506e2c4 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -3,6 +3,9 @@ ## 4.6.0-beta.1 (Unreleased) Upgrade Spring Boot dependencies version to 2.7.7 and Spring Cloud dependencies version to 2021.0.5 +#### Features Added +- Release the `spring-cloud-azure-starter-redis`. This starter supports Azure hosted Redis service authenticating with Azure AD. + ### Spring Cloud Azure Autoconfigure This section includes changes in `spring-cloud-azure-autoconfigure` module. From 55ed92351953ffd8551f58a8e92ed4889e4dcd90 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 10 Jan 2023 09:40:14 +0800 Subject: [PATCH 17/38] use reflection to get pool enabled value --- .../AzureJedisPasswordlessAutoConfiguration.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java index 497ab9d6254c..12d042044e5c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java @@ -24,10 +24,13 @@ import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnection; import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPoolConfig; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.util.Properties; @@ -109,7 +112,17 @@ private void customizeConfigurationFromUrl(RedisProperties redisProperties, Jedi } private boolean isPoolEnabled(RedisProperties.Pool pool) { - Boolean enabled = pool.getEnabled(); + Boolean enabled = true; + Method method = ReflectionUtils.findMethod(RedisProperties.Pool.class, "getEnabled"); + if (method != null) { + try { + enabled = (Boolean) method.invoke(pool); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } return (enabled != null) ? enabled : COMMONS_POOL2_AVAILABLE; } From 1555058650f96e16114eacacbf7a9eac201a4d46 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 10 Jan 2023 10:05:51 +0800 Subject: [PATCH 18/38] use reflection to set pool values --- ...ureJedisPasswordlessAutoConfiguration.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java index 12d042044e5c..6be3daa11687 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java @@ -136,11 +136,35 @@ private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) { config.setMaxTotal(pool.getMaxActive()); config.setMaxIdle(pool.getMaxIdle()); config.setMinIdle(pool.getMinIdle()); + if (pool.getTimeBetweenEvictionRuns() != null) { - config.setTimeBetweenEvictionRuns(pool.getTimeBetweenEvictionRuns()); + Method method = ReflectionUtils.findMethod(RedisProperties.Pool.class, "setTimeBetweenEvictionRuns"); + if (method != null) { + try { + method.invoke(pool, pool.getTimeBetweenEvictionRuns()); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } else { + config.setTimeBetweenEvictionRunsMillis(pool.getTimeBetweenEvictionRuns().toMillis()); + } } + if (pool.getMaxWait() != null) { - config.setMaxWait(pool.getMaxWait()); + Method method = ReflectionUtils.findMethod(RedisProperties.Pool.class, "setMaxWait"); + if (method != null) { + try { + method.invoke(pool, pool.getMaxWait()); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } else { + config.setMaxWaitMillis(pool.getMaxWait().toMillis()); + } } return config; } From 998fcaaa4280292e910e7ec2812a32c3c9cc08c2 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 10 Jan 2023 10:22:00 +0800 Subject: [PATCH 19/38] address PR comments --- .../redis/passwordless/jedis/AzureJedisConnection.java | 2 +- .../passwordless/jedis/AzureJedisConnectionFactory.java | 2 +- .../passwordless/jedis/AzureRedisCredentialSupplier.java | 2 +- .../AzureJedisPasswordlessAutoConfiguration.java | 4 +++- .../passwordless/{jedis => }/RedisUrlSyntaxException.java | 2 +- .../redis/passwordless/{jedis => }/package-info.java | 2 +- .../src/main/resources/META-INF/spring.factories | 2 +- .../jedis/AzureJedisConnectionFactoryTest.java | 2 +- .../jedis/AzureRedisCredentialSupplierTest.java | 2 +- .../AzureJedisPasswordlessAutoConfigurationTest.java | 7 ++++--- 10 files changed, 15 insertions(+), 12 deletions(-) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/{ => implementation}/redis/passwordless/jedis/AzureJedisConnection.java (89%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/{ => implementation}/redis/passwordless/jedis/AzureJedisConnectionFactory.java (99%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/{ => implementation}/redis/passwordless/jedis/AzureRedisCredentialSupplier.java (96%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/{jedis => }/AzureJedisPasswordlessAutoConfiguration.java (97%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/{jedis => }/RedisUrlSyntaxException.java (89%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/{jedis => }/package-info.java (72%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/{ => implementation}/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java (99%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/{ => implementation}/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java (89%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/{jedis => }/AzureJedisPasswordlessAutoConfigurationTest.java (96%) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnection.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnection.java similarity index 89% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnection.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnection.java index eb00bfe825b5..99f6d8583cf5 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnection.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnection.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; import org.springframework.data.redis.connection.jedis.JedisConnection; import org.springframework.lang.Nullable; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java similarity index 99% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java index 327ae252198c..cb90cf57be8e 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; import com.azure.spring.cloud.service.implementation.redis.AzureJedisClientConfig; import com.azure.spring.cloud.service.implementation.redis.AzureJedisPool; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplier.java similarity index 96% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplier.java index 6537a7b814af..e3a5070381a9 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplier.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplier.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; import com.azure.core.credential.TokenCredential; import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java similarity index 97% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java index 6be3daa11687..c1a3430fdb9b 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.redis.passwordless; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis.AzureJedisConnectionFactory; +import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis.AzureRedisCredentialSupplier; import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; import org.apache.commons.pool2.impl.GenericObjectPool; import org.springframework.beans.factory.ObjectProvider; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/RedisUrlSyntaxException.java similarity index 89% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/RedisUrlSyntaxException.java index 53b1c4fd7ff0..3beed220fb2b 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/RedisUrlSyntaxException.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/RedisUrlSyntaxException.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.redis.passwordless; class RedisUrlSyntaxException extends RuntimeException { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/package-info.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/package-info.java similarity index 72% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/package-info.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/package-info.java index b977b05d833f..09e72531cdcb 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/package-info.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/package-info.java @@ -4,4 +4,4 @@ /** * Spring Cloud Azure's auto-configuration for Azure Redis passwordless connections. */ -package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.redis.passwordless; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories index 4e09564aa2f7..3337abed84d1 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories @@ -39,7 +39,7 @@ com.azure.spring.cloud.autoconfigure.resourcemanager.AzureServiceBusResourceMana com.azure.spring.cloud.autoconfigure.resourcemanager.AzureStorageQueueResourceManagerAutoConfiguration,\ com.azure.spring.cloud.autoconfigure.compatibility.AzureCompatibilityVerifierAutoConfiguration,\ com.azure.spring.cloud.autoconfigure.jdbc.AzureJdbcAutoConfiguration,\ -com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis.AzureJedisPasswordlessAutoConfiguration +com.azure.spring.cloud.autoconfigure.redis.passwordless.AzureJedisPasswordlessAutoConfiguration org.springframework.boot.diagnostics.FailureAnalyzer=\ com.azure.spring.cloud.autoconfigure.implementation.compatibility.AzureCompatibilityNotMetFailureAnalyzer diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java similarity index 99% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java index 680b76a32e4f..cb54ba7b8dc9 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; import com.azure.spring.cloud.core.implementation.util.ReflectionUtils; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java similarity index 89% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java index 3fb8de30415d..6e3c53ce9109 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; import org.junit.jupiter.api.Assertions; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java similarity index 96% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java index f453bce26c54..e0afa0135d26 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/jedis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.redis.passwordless; +import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis.AzureJedisConnectionFactory; import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -163,8 +164,8 @@ void testRedisConfigurationWithPool() { assertThat(cf.getPoolConfig().getMinIdle()).isEqualTo(1); assertThat(cf.getPoolConfig().getMaxIdle()).isEqualTo(4); assertThat(cf.getPoolConfig().getMaxTotal()).isEqualTo(16); - assertThat(cf.getPoolConfig().getMaxWaitDuration()).isEqualTo(Duration.ofSeconds(2)); - assertThat(cf.getPoolConfig().getDurationBetweenEvictionRuns()).isEqualTo(Duration.ofSeconds(30)); + assertThat(cf.getPoolConfig().getMaxWaitMillis()).isEqualTo(Duration.ofSeconds(2).toMillis()); + assertThat(cf.getPoolConfig().getTimeBetweenEvictionRunsMillis()).isEqualTo(Duration.ofSeconds(30).toMillis()); }); } From 5c395e71fd6dccd661a10bfe55a4554ed8799b53 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 10 Jan 2023 10:59:53 +0800 Subject: [PATCH 20/38] update compatibility script --- sdk/spring/scripts/compatibility_delete_version.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/spring/scripts/compatibility_delete_version.py b/sdk/spring/scripts/compatibility_delete_version.py index 9fc0e08fab7d..e55b11a8e4f2 100644 --- a/sdk/spring/scripts/compatibility_delete_version.py +++ b/sdk/spring/scripts/compatibility_delete_version.py @@ -18,7 +18,9 @@ IGNORED_ARTIFACTS = {'com.github.tomakehurst:wiremock-jre8'} IGNORED_SPRINGBOOT_ARTIFACTS = { "2.5.14": {"org.postgresql:postgresql", - "com.mysql:mysql-connector-j"} + "com.mysql:mysql-connector-j", + "org.mockito:mockito-core", + "org.mockito:mockito-junit-jupiter"} } def get_args(): From 4cd84675c13ad06b818ac77dcc392baa02ac2ce0 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 10 Jan 2023 11:36:32 +0800 Subject: [PATCH 21/38] fix pipeline errors --- sdk/spring/scripts/compatibility_delete_version.py | 4 +--- .../AzureJedisPasswordlessAutoConfiguration.java | 3 +++ .../AzureJedisPasswordlessAutoConfigurationTest.java | 12 +++++------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/sdk/spring/scripts/compatibility_delete_version.py b/sdk/spring/scripts/compatibility_delete_version.py index e55b11a8e4f2..9fc0e08fab7d 100644 --- a/sdk/spring/scripts/compatibility_delete_version.py +++ b/sdk/spring/scripts/compatibility_delete_version.py @@ -18,9 +18,7 @@ IGNORED_ARTIFACTS = {'com.github.tomakehurst:wiremock-jre8'} IGNORED_SPRINGBOOT_ARTIFACTS = { "2.5.14": {"org.postgresql:postgresql", - "com.mysql:mysql-connector-j", - "org.mockito:mockito-core", - "org.mockito:mockito-junit-jupiter"} + "com.mysql:mysql-connector-j"} } def get_args(): diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java index c1a3430fdb9b..d5d4fccaa975 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java @@ -114,6 +114,9 @@ private void customizeConfigurationFromUrl(RedisProperties redisProperties, Jedi } private boolean isPoolEnabled(RedisProperties.Pool pool) { + if (pool == null) { + return false; + } Boolean enabled = true; Method method = ReflectionUtils.findMethod(RedisProperties.Pool.class, "getEnabled"); if (method != null) { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java index e0afa0135d26..aa4f66875e55 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java @@ -20,8 +20,6 @@ import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; /** * Tests for {@link AzureJedisPasswordlessAutoConfiguration}. @@ -85,8 +83,7 @@ void connectionFactoryDefaultsToJedis() { @Test @SuppressWarnings("unchecked") void testGetPasswordFromSupplier() { - Supplier mockCredentialSupplier = mock(Supplier.class); - when(mockCredentialSupplier.get()).thenReturn("fake-password-from-mock-supplier"); + Supplier mockCredentialSupplier = () -> "fake-password-from-mock-supplier"; this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1") .withBean("azureRedisCredentialSupplier", Supplier.class, () -> mockCredentialSupplier, beanDefinition -> beanDefinition.setPrimary(true)) .run((context) -> { @@ -100,9 +97,10 @@ void testGetPasswordFromSupplier() { @Test void testUseSsl() { - this.contextRunner.run((context) -> { - AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.isUseSsl()).isTrue(); + this.contextRunner + .run((context) -> { + AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); + assertThat(cf.isUseSsl()).isTrue(); }); } From c03756aac5feb7b64dd55012c13a33c8df0f28a4 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 10 Jan 2023 12:01:33 +0800 Subject: [PATCH 22/38] fix pipeline errors --- .../AzureJedisPasswordlessAutoConfigurationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java index aa4f66875e55..98ed80ad7a80 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java @@ -101,7 +101,7 @@ void testUseSsl() { .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); assertThat(cf.isUseSsl()).isTrue(); - }); + }); } @Test From f6934fc632df82bebb7e39c8afb10478bab0cb0c Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 10 Jan 2023 13:54:13 +0800 Subject: [PATCH 23/38] update MemoryOptions for test --- eng/pipelines/templates/variables/globals.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/pipelines/templates/variables/globals.yml b/eng/pipelines/templates/variables/globals.yml index 507556fe7e6d..73584a160221 100644 --- a/eng/pipelines/templates/variables/globals.yml +++ b/eng/pipelines/templates/variables/globals.yml @@ -20,7 +20,7 @@ variables: WagonOptions: '-Dmaven.wagon.httpconnectionManager.ttlSeconds=60' DefaultOptions: '-Dmaven.repo.local=$(MAVEN_CACHE_FOLDER) --batch-mode --fail-at-end --settings eng/settings.xml $(WagonOptions)' LoggingOptions: '-Dorg.slf4j.simpleLogger.defaultLogLevel=$(MavenLogLevel) -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn' - MemoryOptions: '-Xmx3072m' + MemoryOptions: '-Xmx4096m' DefaultSkipOptions: '-Dgpg.skip -Dmaven.javadoc.skip=true -Dcodesnippet.skip=true -Dspotbugs.skip=true -Dcheckstyle.skip=true -Drevapi.skip=true -DtrimStackTrace=false' DefaultTestOptions: '$(DefaultOptions) $(DefaultSkipOptions) -pl $(ProjectList)' DefaultTestMavenOptions: '$(MemoryOptions) $(LoggingOptions)' From eef1ba2465792ec0ddcac014f204b5f05bbb9afa Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 10 Jan 2023 16:35:23 +0800 Subject: [PATCH 24/38] add redis test --- eng/versioning/external_dependencies.txt | 4 +- .../pom.xml | 24 +++++++ .../tests/redis/AzureRedisPasswordlessIT.java | 65 +++++++++++++++++++ .../src/test/resources/application-redis.yml | 6 ++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java create mode 100644 sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-redis.yml diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index 0893e5895dca..3f22c94c9da9 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -313,6 +313,8 @@ org.revapi:revapi-java;0.26.1 org.revapi:revapi-reporter-json;0.4.5 org.revapi:revapi-maven-plugin;0.14.6 org.sonatype.plugins:nexus-staging-maven-plugin;1.6.8 +org.testcontainers:testcontainers;1.17.6 +org.testcontainers:junit-jupiter;1.17.6 # External Dependency Exceptions # This section is for external dependencies whose versions were different than @@ -400,4 +402,4 @@ storage_com.microsoft.azure:azure-storage;8.4.0 spring_com.microsoft.azure:azure;1.34.0 # sdk\spring\spring-cloud-azure-service\pom.xml -spring_redis.clients:jedis;3.8.0 \ No newline at end of file +spring_redis.clients:jedis;3.8.0 diff --git a/sdk/spring/spring-cloud-azure-integration-tests/pom.xml b/sdk/spring/spring-cloud-azure-integration-tests/pom.xml index 6eb30a574976..aa322523f06e 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/pom.xml +++ b/sdk/spring/spring-cloud-azure-integration-tests/pom.xml @@ -119,6 +119,30 @@ h2 test + + + + org.testcontainers + testcontainers + 1.17.6 + test + + + + org.testcontainers + junit-jupiter + 1.17.6 + test + + + + + org.springframework.data + spring-data-redis + 2.7.6 + test + + diff --git a/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java b/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java new file mode 100644 index 000000000000..2057c2d9bb91 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.integration.tests.redis; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@Testcontainers +@ActiveProfiles("redis") +public class AzureRedisPasswordlessIT { + + @Autowired + private RedisTemplate redisTemplate; + + private static final String REDIS_PASSWORD = "fake-testcontainer-password"; + + @Bean(name = "azureRedisCredentialSupplier") + Supplier redisCredential() { + return () -> REDIS_PASSWORD; + } + + @Container + private static GenericContainer redis = + new GenericContainer<>(DockerImageName.parse("redis:5.0.3-alpine")) + .withCommand("--requirepass", REDIS_PASSWORD) + .withExposedPorts(6379); + + + @DynamicPropertySource + static void redisProperties(DynamicPropertyRegistry registry) { + registry.add("spring.redis.host", redis::getHost); + registry.add("spring.redis.port", redis::getFirstMappedPort); + registry.add("spring.redis.azure.passwordless-enabled", () -> true); + } + + @Test + public void testRedisTemplate() { + Map valueMap = new HashMap(); + valueMap.put("valueMap1", "map1"); + valueMap.put("valueMap2", "map2"); + valueMap.put("valueMap3", "map3"); + redisTemplate.opsForValue().multiSet(valueMap); + String value = (String) redisTemplate.opsForValue().get("valueMap2"); + Assertions.assertEquals("map2", value); + } + +} + + diff --git a/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-redis.yml b/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-redis.yml new file mode 100644 index 000000000000..e71b97f3e6c7 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-redis.yml @@ -0,0 +1,6 @@ +spring: + redis: + username: redis-test-container-host + host: redis-test-container-host + azure: + passwordless-enabled: true From c06f3f79a2ab7c3f15ec05e645e7d4adafd83289 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 10 Jan 2023 17:54:47 +0800 Subject: [PATCH 25/38] address some comments --- .../jedis/AzureRedisCredentialSupplier.java | 26 +- ...ureJedisPasswordlessAutoConfiguration.java | 214 +---------------- .../AzureJedisPasswordlessUtil.java | 225 ++++++++++++++++++ .../pom.xml | 7 +- .../tests/redis/AzureRedisPasswordlessIT.java | 3 + .../AzurePasswordlessProperties.java | 4 +- 6 files changed, 239 insertions(+), 240 deletions(-) create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessUtil.java diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplier.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplier.java index e3a5070381a9..ec1e62c2636f 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplier.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplier.java @@ -3,10 +3,7 @@ package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; -import com.azure.core.credential.TokenCredential; -import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; -import com.azure.identity.extensions.implementation.token.AccessTokenResolver; import java.util.Properties; import java.util.function.Supplier; @@ -25,31 +22,10 @@ public class AzureRedisCredentialSupplier implements Supplier { * @param properties properties to initialize AzureRedisCredentialSupplier. */ public AzureRedisCredentialSupplier(Properties properties) { - this(properties, null, null); - } - - /** - * Create {@link AzureRedisCredentialSupplier} instance. - * @param properties properties to initialize AzureRedisCredentialSupplier. - * @param tokenCredentialProvider Supplier that provide a {@link TokenCredential}. - * @param accessTokenResolver An {@link AccessTokenResolver} instance, which will take a TokenCredential as input - * and outputs a publisher that emits a single access token. - */ - public AzureRedisCredentialSupplier(Properties properties, TokenCredentialProvider tokenCredentialProvider, AccessTokenResolver accessTokenResolver) { - azureAuthenticationTemplate = new AzureAuthenticationTemplate(tokenCredentialProvider, accessTokenResolver); + azureAuthenticationTemplate = new AzureAuthenticationTemplate(); azureAuthenticationTemplate.init(properties); } - /** - * Create {@link AzureRedisCredentialSupplier} instance. - * @param tokenCredentialProvider Supplier that provide a {@link TokenCredential}, must not be {@literal null}. - * @param accessTokenResolver An {@link AccessTokenResolver} instance, which will take a TokenCredential as input - * and outputs a publisher that emits a single access token, must not be {@literal null}. - */ - public AzureRedisCredentialSupplier(TokenCredentialProvider tokenCredentialProvider, AccessTokenResolver accessTokenResolver) { - this(null, tokenCredentialProvider, accessTokenResolver); - } - @Override public String get() { return azureAuthenticationTemplate.getTokenAsPassword(); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java index d5d4fccaa975..e85646fa7630 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java @@ -17,29 +17,20 @@ import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.RedisPassword; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnection; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPoolConfig; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URI; -import java.net.URISyntaxException; import java.util.Properties; import java.util.function.Supplier; -import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreNull; -import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreTargetNonNull; +import static com.azure.spring.cloud.autoconfigure.redis.passwordless.AzureJedisPasswordlessUtil.getJedisClientConfiguration; +import static com.azure.spring.cloud.autoconfigure.redis.passwordless.AzureJedisPasswordlessUtil.getStandaloneConfig; +import static com.azure.spring.cloud.autoconfigure.redis.passwordless.AzureJedisPasswordlessUtil.mergeAzureProperties; /** * Azure Redis passwordless connection configuration using Jedis. @@ -54,13 +45,8 @@ @EnableConfigurationProperties(RedisProperties.class) class AzureJedisPasswordlessAutoConfiguration { - private static final boolean COMMONS_POOL2_AVAILABLE = ClassUtils.isPresent("org.apache.commons.pool2.ObjectPool", - AzureJedisPasswordlessAutoConfiguration.class.getClassLoader()); - private static final String AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME = "azureRedisCredentialSupplier"; - private static final int AZURE_REDIS_PORT = 6380; - @Bean @ConfigurationProperties(prefix = "spring.redis.azure") AzureRedisPasswordlessProperties redisPasswordlessProperties() { @@ -82,198 +68,4 @@ AzureJedisConnectionFactory azureRedisConnectionFactory(RedisProperties redisPro return new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, azureRedisCredentialSupplier); } - private JedisClientConfiguration getJedisClientConfiguration(RedisProperties redisProperties) { - - JedisClientConfiguration.JedisClientConfigurationBuilder builder = applyProperties(redisProperties, JedisClientConfiguration.builder()); - builder.useSsl(); - RedisProperties.Pool pool = redisProperties.getJedis().getPool(); - - if (isPoolEnabled(pool)) { - applyPooling(pool, builder); - } - - if (StringUtils.hasText(redisProperties.getUrl())) { - customizeConfigurationFromUrl(redisProperties, builder); - } - return builder.build(); - } - - private JedisClientConfiguration.JedisClientConfigurationBuilder applyProperties(RedisProperties properties, JedisClientConfiguration.JedisClientConfigurationBuilder builder) { - PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); - map.from(properties.getTimeout()).to(builder::readTimeout); - map.from(properties.getConnectTimeout()).to(builder::connectTimeout); - map.from(properties.getClientName()).whenHasText().to(builder::clientName); - return builder; - } - - private void customizeConfigurationFromUrl(RedisProperties redisProperties, JedisClientConfiguration.JedisClientConfigurationBuilder builder) { - ConnectionInfo connectionInfo = parseUrl(redisProperties.getUrl()); - if (connectionInfo.isUseSsl()) { - builder.useSsl(); - } - } - - private boolean isPoolEnabled(RedisProperties.Pool pool) { - if (pool == null) { - return false; - } - Boolean enabled = true; - Method method = ReflectionUtils.findMethod(RedisProperties.Pool.class, "getEnabled"); - if (method != null) { - try { - enabled = (Boolean) method.invoke(pool); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } - return (enabled != null) ? enabled : COMMONS_POOL2_AVAILABLE; - } - - private void applyPooling(RedisProperties.Pool pool, - JedisClientConfiguration.JedisClientConfigurationBuilder builder) { - builder.usePooling().poolConfig(jedisPoolConfig(pool)); - } - - private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) { - JedisPoolConfig config = new JedisPoolConfig(); - config.setMaxTotal(pool.getMaxActive()); - config.setMaxIdle(pool.getMaxIdle()); - config.setMinIdle(pool.getMinIdle()); - - if (pool.getTimeBetweenEvictionRuns() != null) { - Method method = ReflectionUtils.findMethod(RedisProperties.Pool.class, "setTimeBetweenEvictionRuns"); - if (method != null) { - try { - method.invoke(pool, pool.getTimeBetweenEvictionRuns()); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } else { - config.setTimeBetweenEvictionRunsMillis(pool.getTimeBetweenEvictionRuns().toMillis()); - } - } - - if (pool.getMaxWait() != null) { - Method method = ReflectionUtils.findMethod(RedisProperties.Pool.class, "setMaxWait"); - if (method != null) { - try { - method.invoke(pool, pool.getMaxWait()); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } else { - config.setMaxWaitMillis(pool.getMaxWait().toMillis()); - } - } - return config; - } - - private AzureRedisPasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureRedisPasswordlessProperties redisPasswordlessProperties) { - AzureRedisPasswordlessProperties target = new AzureRedisPasswordlessProperties(); - copyPropertiesIgnoreNull(redisPasswordlessProperties.getScopes(), target.getScopes()); - copyPropertiesIgnoreNull(redisPasswordlessProperties.getCredential(), target.getCredential()); - copyPropertiesIgnoreNull(redisPasswordlessProperties.getProfile(), target.getProfile()); - copyPropertiesIgnoreNull(redisPasswordlessProperties.getClient(), target.getClient()); - copyPropertiesIgnoreNull(redisPasswordlessProperties.getProxy(), target.getProxy()); - - if (azureGlobalProperties != null) { - copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getProfile(), target.getProfile()); - copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getCredential(), target.getCredential()); - } - - return redisPasswordlessProperties; - } - - private RedisStandaloneConfiguration getStandaloneConfig(RedisProperties redisProperties) { - RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); - if (StringUtils.hasText(redisProperties.getUrl())) { - ConnectionInfo connectionInfo = parseUrl(redisProperties.getUrl()); - config.setHostName(connectionInfo.getHostName()); - config.setPort(connectionInfo.getPort()); - config.setUsername(connectionInfo.getUsername()); - config.setPassword(RedisPassword.of(connectionInfo.getPassword())); - } else { - config.setHostName(redisProperties.getHost()); - config.setPort(redisProperties.getPort()); - config.setUsername(redisProperties.getUsername()); - config.setPassword(RedisPassword.of(redisProperties.getPassword())); - } - config.setDatabase(redisProperties.getDatabase()); - if (config.getPort() == 0) { - config.setPort(AZURE_REDIS_PORT); - } - return config; - } - - ConnectionInfo parseUrl(String url) { - try { - URI uri = new URI(url); - String scheme = uri.getScheme(); - if (!"redis".equals(scheme) && !"rediss".equals(scheme)) { - throw new RedisUrlSyntaxException(url); - } - boolean useSsl = ("rediss".equals(scheme)); - String username = null; - String password = null; - if (uri.getUserInfo() != null) { - String candidate = uri.getUserInfo(); - int index = candidate.indexOf(':'); - if (index >= 0) { - username = candidate.substring(0, index); - password = candidate.substring(index + 1); - } else { - password = candidate; - } - } - return new ConnectionInfo(uri, useSsl, username, password); - } catch (URISyntaxException ex) { - throw new RedisUrlSyntaxException(url, ex); - } - } - - static class ConnectionInfo { - - private final URI uri; - - private final boolean useSsl; - - private final String username; - - private final String password; - - ConnectionInfo(URI uri, boolean useSsl, String username, String password) { - this.uri = uri; - this.useSsl = useSsl; - this.username = username; - this.password = password; - } - - boolean isUseSsl() { - return this.useSsl; - } - - String getHostName() { - return this.uri.getHost(); - } - - int getPort() { - return this.uri.getPort(); - } - - String getUsername() { - return this.username; - } - - String getPassword() { - return this.password; - } - - } - } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessUtil.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessUtil.java new file mode 100644 index 000000000000..d5b72d0e5f63 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessUtil.java @@ -0,0 +1,225 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.redis.passwordless; + +import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.data.redis.connection.RedisPassword; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; +import redis.clients.jedis.JedisPoolConfig; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; + +import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreNull; +import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreTargetNonNull; + +final class AzureJedisPasswordlessUtil { + + private static final int AZURE_REDIS_PORT = 6380; + private static final boolean COMMONS_POOL2_AVAILABLE = ClassUtils.isPresent("org.apache.commons.pool2.ObjectPool", + AzureJedisPasswordlessAutoConfiguration.class.getClassLoader()); + + static AzureRedisPasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureRedisPasswordlessProperties redisPasswordlessProperties) { + AzureRedisPasswordlessProperties target = new AzureRedisPasswordlessProperties(); + copyPropertiesIgnoreNull(redisPasswordlessProperties.getScopes(), target.getScopes()); + copyPropertiesIgnoreNull(redisPasswordlessProperties.getCredential(), target.getCredential()); + copyPropertiesIgnoreNull(redisPasswordlessProperties.getProfile(), target.getProfile()); + copyPropertiesIgnoreNull(redisPasswordlessProperties.getClient(), target.getClient()); + copyPropertiesIgnoreNull(redisPasswordlessProperties.getProxy(), target.getProxy()); + + if (azureGlobalProperties != null) { + copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getProfile(), target.getProfile()); + copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getCredential(), target.getCredential()); + } + + return redisPasswordlessProperties; + } + + static JedisClientConfiguration getJedisClientConfiguration(RedisProperties redisProperties) { + + JedisClientConfiguration.JedisClientConfigurationBuilder builder = applyProperties(redisProperties, JedisClientConfiguration.builder()); + builder.useSsl(); + RedisProperties.Pool pool = redisProperties.getJedis().getPool(); + + if (isPoolEnabled(pool)) { + applyPooling(pool, builder); + } + + if (StringUtils.hasText(redisProperties.getUrl())) { + customizeConfigurationFromUrl(redisProperties, builder); + } + return builder.build(); + } + + private static JedisClientConfiguration.JedisClientConfigurationBuilder applyProperties(RedisProperties properties, JedisClientConfiguration.JedisClientConfigurationBuilder builder) { + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(properties.getTimeout()).to(builder::readTimeout); + map.from(properties.getConnectTimeout()).to(builder::connectTimeout); + map.from(properties.getClientName()).whenHasText().to(builder::clientName); + return builder; + } + + private static void customizeConfigurationFromUrl(RedisProperties redisProperties, JedisClientConfiguration.JedisClientConfigurationBuilder builder) { + ConnectionInfo connectionInfo = parseUrl(redisProperties.getUrl()); + if (connectionInfo.isUseSsl()) { + builder.useSsl(); + } + } + + private static boolean isPoolEnabled(RedisProperties.Pool pool) { + if (pool == null) { + return false; + } + Boolean enabled = true; + Method method = ReflectionUtils.findMethod(RedisProperties.Pool.class, "getEnabled"); + if (method != null) { + try { + enabled = (Boolean) method.invoke(pool); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + return (enabled != null) ? enabled : COMMONS_POOL2_AVAILABLE; + } + + private static void applyPooling(RedisProperties.Pool pool, + JedisClientConfiguration.JedisClientConfigurationBuilder builder) { + builder.usePooling().poolConfig(jedisPoolConfig(pool)); + } + + private static JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) { + JedisPoolConfig config = new JedisPoolConfig(); + config.setMaxTotal(pool.getMaxActive()); + config.setMaxIdle(pool.getMaxIdle()); + config.setMinIdle(pool.getMinIdle()); + + if (pool.getTimeBetweenEvictionRuns() != null) { + Method method = ReflectionUtils.findMethod(RedisProperties.Pool.class, "setTimeBetweenEvictionRuns"); + if (method != null) { + try { + method.invoke(pool, pool.getTimeBetweenEvictionRuns()); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } else { + config.setTimeBetweenEvictionRunsMillis(pool.getTimeBetweenEvictionRuns().toMillis()); + } + } + + if (pool.getMaxWait() != null) { + Method method = ReflectionUtils.findMethod(RedisProperties.Pool.class, "setMaxWait"); + if (method != null) { + try { + method.invoke(pool, pool.getMaxWait()); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } else { + config.setMaxWaitMillis(pool.getMaxWait().toMillis()); + } + } + return config; + } + + static RedisStandaloneConfiguration getStandaloneConfig(RedisProperties redisProperties) { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); + if (StringUtils.hasText(redisProperties.getUrl())) { + ConnectionInfo connectionInfo = parseUrl(redisProperties.getUrl()); + config.setHostName(connectionInfo.getHostName()); + config.setPort(connectionInfo.getPort()); + config.setUsername(connectionInfo.getUsername()); + config.setPassword(RedisPassword.of(connectionInfo.getPassword())); + } else { + config.setHostName(redisProperties.getHost()); + config.setPort(redisProperties.getPort()); + config.setUsername(redisProperties.getUsername()); + config.setPassword(RedisPassword.of(redisProperties.getPassword())); + } + config.setDatabase(redisProperties.getDatabase()); + if (config.getPort() == 0) { + config.setPort(AZURE_REDIS_PORT); + } + return config; + } + + static ConnectionInfo parseUrl(String url) { + try { + URI uri = new URI(url); + String scheme = uri.getScheme(); + if (!"redis".equals(scheme) && !"rediss".equals(scheme)) { + throw new RedisUrlSyntaxException(url); + } + boolean useSsl = ("rediss".equals(scheme)); + String username = null; + String password = null; + if (uri.getUserInfo() != null) { + String candidate = uri.getUserInfo(); + int index = candidate.indexOf(':'); + if (index >= 0) { + username = candidate.substring(0, index); + password = candidate.substring(index + 1); + } else { + password = candidate; + } + } + return new ConnectionInfo(uri, useSsl, username, password); + } catch (URISyntaxException ex) { + throw new RedisUrlSyntaxException(url, ex); + } + } + + static class ConnectionInfo { + + private final URI uri; + + private final boolean useSsl; + + private final String username; + + private final String password; + + ConnectionInfo(URI uri, boolean useSsl, String username, String password) { + this.uri = uri; + this.useSsl = useSsl; + this.username = username; + this.password = password; + } + + boolean isUseSsl() { + return this.useSsl; + } + + String getHostName() { + return this.uri.getHost(); + } + + int getPort() { + return this.uri.getPort(); + } + + String getUsername() { + return this.username; + } + + String getPassword() { + return this.password; + } + + } +} diff --git a/sdk/spring/spring-cloud-azure-integration-tests/pom.xml b/sdk/spring/spring-cloud-azure-integration-tests/pom.xml index aa322523f06e..26c0f75e4c70 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/pom.xml +++ b/sdk/spring/spring-cloud-azure-integration-tests/pom.xml @@ -100,7 +100,12 @@ com.azure.spring spring-cloud-azure-starter-jdbc-mysql - 4.6.0-beta.1 + 4.6.0-beta.1 + + + com.azure.spring + spring-cloud-azure-starter-redis + 4.6.0-beta.1 org.springframework.boot diff --git a/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java b/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java index 2057c2d9bb91..faa39853c9c4 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java +++ b/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java @@ -4,6 +4,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; @@ -23,6 +25,7 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @Testcontainers @ActiveProfiles("redis") +@DisabledOnOs({OS.WINDOWS, OS.MAC}) public class AzureRedisPasswordlessIT { @Autowired diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java index a15030d88c05..ba4e92f2e04d 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java @@ -4,7 +4,6 @@ package com.azure.spring.cloud.service.implementation.passwordless; import com.azure.identity.extensions.implementation.enums.AuthProperty; -import com.azure.spring.cloud.core.implementation.properties.PropertyMapper; import com.azure.spring.cloud.core.properties.AzureProperties; import com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; import com.azure.spring.cloud.core.properties.client.ClientProperties; @@ -22,7 +21,6 @@ * support for other third party services. */ public class AzurePasswordlessProperties implements AzureProperties { - private static final PropertyMapper PROPERTY_MAPPER = new PropertyMapper(); private AzureProfileProperties profile = new AzureProfileProperties(); @@ -94,7 +92,7 @@ public void setScopes(String scopes) { public Properties toProperties() { Properties target = new Properties(); for (AzurePasswordlessPropertiesMapping m : AzurePasswordlessPropertiesMapping.values()) { - PROPERTY_MAPPER.from(m.getter().apply(this)).to(v -> m.setter.accept(target, v)); + m.setter.accept(target, m.getter.apply(this)); } return target; } From cfa2989d0a5a70a317ca06a5794b156afe518298 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 11 Jan 2023 09:31:39 +0800 Subject: [PATCH 26/38] fix pipeline errors --- .../passwordless/AzurePasswordlessProperties.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java index ba4e92f2e04d..53b08b91327a 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java @@ -92,7 +92,9 @@ public void setScopes(String scopes) { public Properties toProperties() { Properties target = new Properties(); for (AzurePasswordlessPropertiesMapping m : AzurePasswordlessPropertiesMapping.values()) { - m.setter.accept(target, m.getter.apply(this)); + if (m.getter.apply(this) != null) { + m.setter.accept(target, m.getter.apply(this)); + } } return target; } From 32adfeeb55509c2985ca473816965f91f3262fe5 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 11 Jan 2023 10:52:16 +0800 Subject: [PATCH 27/38] address comments --- .../spring-cloud-azure-autoconfigure/pom.xml | 15 +++ .../jedis/AzureJedisConnectionFactory.java | 92 ----------------- ...ureJedisPasswordlessAutoConfiguration.java | 17 ++-- .../AzureJedisPasswordlessUtil.java | 5 +- .../RedisUrlSyntaxException.java | 2 +- .../redis/passwordless/package-info.java | 7 -- .../main/resources/META-INF/spring.factories | 2 +- .../redis/AzureRedisPasswordlessUT.java} | 29 ++++-- .../AzureJedisConnectionFactoryTest.java | 60 +++-------- ...edisPasswordlessAutoConfigurationTest.java | 99 ++++++++++++------- .../src/test/resources/application-redis.yml | 5 +- .../pom.xml | 23 ----- 12 files changed, 130 insertions(+), 226 deletions(-) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/{passwordless => }/AzureJedisPasswordlessAutoConfiguration.java (83%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/{passwordless => }/AzureJedisPasswordlessUtil.java (98%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/{passwordless => }/RedisUrlSyntaxException.java (90%) delete mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/package-info.java rename sdk/spring/{spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java => spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java} (71%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/{passwordless => }/AzureJedisPasswordlessAutoConfigurationTest.java (59%) rename sdk/spring/{spring-cloud-azure-integration-tests => spring-cloud-azure-autoconfigure}/src/test/resources/application-redis.yml (56%) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml b/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml index afadd43507d1..0d088f449ed3 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml +++ b/sdk/spring/spring-cloud-azure-autoconfigure/pom.xml @@ -351,6 +351,21 @@ test + + + org.testcontainers + testcontainers + 1.17.6 + test + + + + org.testcontainers + junit-jupiter + 1.17.6 + test + + diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java index cb90cf57be8e..927ce0e393a6 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -5,7 +5,6 @@ import com.azure.spring.cloud.service.implementation.redis.AzureJedisClientConfig; import com.azure.spring.cloud.service.implementation.redis.AzureJedisPool; -import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; @@ -145,93 +144,6 @@ public boolean getUsePool() { return clientConfiguration.isUsePooling(); } - /** - * Returns the index of the database. - * - * @return the database index. - */ - public int getDatabase() { - return RedisConfiguration.getDatabaseOrElse(standaloneConfig, standaloneConfig::getDatabase); - } - - /** - * Returns the port used to connect to the Redis instance. - * - * @return the Redis port. - */ - public int getPort() { - return standaloneConfig.getPort(); - } - - /** - * @return {@literal true} to use SSL, {@literal false} to use unencrypted connections. - */ - public boolean isUseSsl() { - return clientConfiguration.isUseSsl(); - } - - /** - * Returns the Redis hostname. - * - * @return the hostName. - */ - public String getHostName() { - return standaloneConfig.getHostName(); - } - - /** - * Returns the password used for authenticating with the Redis server. - * - * @return password for authentication. - */ - @Nullable - public String getPassword() { - RedisPassword password = getRedisPassword(); - if (password.isPresent()) { - return password.map(String::new).orElse(null); - } else if (credentialSupplier != null) { - return credentialSupplier.get(); - } - return null; - } - - /** - * Returns the poolConfig. - * - * @return the poolConfig - */ - @SuppressWarnings("unchecked") - @Nullable - public GenericObjectPoolConfig getPoolConfig() { - return clientConfiguration.getPoolConfig().orElse(null); - } - - /** - * @return the {@link JedisClientConfiguration}. - */ - public JedisClientConfiguration getClientConfiguration() { - return clientConfiguration; - } - - /** - * Returns the client name. - * - * @return the client name. - */ - @Nullable - public String getClientName() { - return clientConfiguration.getClientName().orElse(null); - } - - /** - * Returns the timeout. - * - * @return the timeout. - */ - public int getTimeout() { - return getReadTimeout(); - } - private JedisConnection postProcessConnection(JedisConnection connection) { return connection; } @@ -306,10 +218,6 @@ private JedisClientConfig createClientConfig(RedisStandaloneConfiguration standa return builder.build(); } - private int getReadTimeout() { - return Math.toIntExact(clientConfiguration.getReadTimeout().toMillis()); - } - private RedisPassword getRedisPassword() { return RedisConfiguration.getPasswordOrElse(this.standaloneConfig, standaloneConfig::getPassword); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java similarity index 83% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java index e85646fa7630..e2ce80695c12 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless; +package com.azure.spring.cloud.autoconfigure.redis; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis.AzureJedisConnectionFactory; @@ -10,16 +10,17 @@ import org.apache.commons.pool2.impl.GenericObjectPool; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnection; @@ -28,9 +29,9 @@ import java.util.Properties; import java.util.function.Supplier; -import static com.azure.spring.cloud.autoconfigure.redis.passwordless.AzureJedisPasswordlessUtil.getJedisClientConfiguration; -import static com.azure.spring.cloud.autoconfigure.redis.passwordless.AzureJedisPasswordlessUtil.getStandaloneConfig; -import static com.azure.spring.cloud.autoconfigure.redis.passwordless.AzureJedisPasswordlessUtil.mergeAzureProperties; +import static com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessUtil.getJedisClientConfiguration; +import static com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessUtil.getStandaloneConfig; +import static com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessUtil.mergeAzureProperties; /** * Azure Redis passwordless connection configuration using Jedis. @@ -40,10 +41,10 @@ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({GenericObjectPool.class, JedisConnection.class, Jedis.class}) @ConditionalOnExpression("${spring.redis.azure.passwordless-enabled:false}") -@ConditionalOnMissingBean(RedisConnectionFactory.class) -@ConditionalOnProperty(prefix = "spring.redis", name = {"host", "username"}) +@AutoConfigureBefore(RedisAutoConfiguration.class) +@ConditionalOnProperty(prefix = "spring.redis", name = {"host"}) @EnableConfigurationProperties(RedisProperties.class) -class AzureJedisPasswordlessAutoConfiguration { +public class AzureJedisPasswordlessAutoConfiguration { private static final String AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME = "azureRedisCredentialSupplier"; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessUtil.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java similarity index 98% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessUtil.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java index d5b72d0e5f63..38df3d77ac3b 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessUtil.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless; +package com.azure.spring.cloud.autoconfigure.redis; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; @@ -48,7 +48,7 @@ static AzureRedisPasswordlessProperties mergeAzureProperties(AzureGlobalProperti static JedisClientConfiguration getJedisClientConfiguration(RedisProperties redisProperties) { JedisClientConfiguration.JedisClientConfigurationBuilder builder = applyProperties(redisProperties, JedisClientConfiguration.builder()); - builder.useSsl(); + RedisProperties.Pool pool = redisProperties.getJedis().getPool(); if (isPoolEnabled(pool)) { @@ -63,6 +63,7 @@ static JedisClientConfiguration getJedisClientConfiguration(RedisProperties redi private static JedisClientConfiguration.JedisClientConfigurationBuilder applyProperties(RedisProperties properties, JedisClientConfiguration.JedisClientConfigurationBuilder builder) { PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(properties.isSsl()).whenTrue().toCall(builder::useSsl); map.from(properties.getTimeout()).to(builder::readTimeout); map.from(properties.getConnectTimeout()).to(builder::connectTimeout); map.from(properties.getClientName()).whenHasText().to(builder::clientName); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/RedisUrlSyntaxException.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/RedisUrlSyntaxException.java similarity index 90% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/RedisUrlSyntaxException.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/RedisUrlSyntaxException.java index 3beed220fb2b..10e294db143b 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/RedisUrlSyntaxException.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/RedisUrlSyntaxException.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless; +package com.azure.spring.cloud.autoconfigure.redis; class RedisUrlSyntaxException extends RuntimeException { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/package-info.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/package-info.java deleted file mode 100644 index 09e72531cdcb..000000000000 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -/** - * Spring Cloud Azure's auto-configuration for Azure Redis passwordless connections. - */ -package com.azure.spring.cloud.autoconfigure.redis.passwordless; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories index 3337abed84d1..cd77a65a8822 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories @@ -39,7 +39,7 @@ com.azure.spring.cloud.autoconfigure.resourcemanager.AzureServiceBusResourceMana com.azure.spring.cloud.autoconfigure.resourcemanager.AzureStorageQueueResourceManagerAutoConfiguration,\ com.azure.spring.cloud.autoconfigure.compatibility.AzureCompatibilityVerifierAutoConfiguration,\ com.azure.spring.cloud.autoconfigure.jdbc.AzureJdbcAutoConfiguration,\ -com.azure.spring.cloud.autoconfigure.redis.passwordless.AzureJedisPasswordlessAutoConfiguration +com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessAutoConfiguration org.springframework.boot.diagnostics.FailureAnalyzer=\ com.azure.spring.cloud.autoconfigure.implementation.compatibility.AzureCompatibilityNotMetFailureAnalyzer diff --git a/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java similarity index 71% rename from sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java index faa39853c9c4..c2cbf94b71a2 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/redis/AzureRedisPasswordlessIT.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java @@ -1,14 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.integration.tests.redis; +package com.azure.spring.cloud.autoconfigure.implementation.redis; + +import com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessAutoConfiguration; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; @@ -26,18 +31,13 @@ @Testcontainers @ActiveProfiles("redis") @DisabledOnOs({OS.WINDOWS, OS.MAC}) -public class AzureRedisPasswordlessIT { +public class AzureRedisPasswordlessUT { @Autowired - private RedisTemplate redisTemplate; + private RedisTemplate redisTemplate; private static final String REDIS_PASSWORD = "fake-testcontainer-password"; - @Bean(name = "azureRedisCredentialSupplier") - Supplier redisCredential() { - return () -> REDIS_PASSWORD; - } - @Container private static GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:5.0.3-alpine")) @@ -49,12 +49,13 @@ Supplier redisCredential() { static void redisProperties(DynamicPropertyRegistry registry) { registry.add("spring.redis.host", redis::getHost); registry.add("spring.redis.port", redis::getFirstMappedPort); + registry.add("spring.redis.ssl", () -> false); registry.add("spring.redis.azure.passwordless-enabled", () -> true); } @Test public void testRedisTemplate() { - Map valueMap = new HashMap(); + Map valueMap = new HashMap<>(); valueMap.put("valueMap1", "map1"); valueMap.put("valueMap2", "map2"); valueMap.put("valueMap3", "map3"); @@ -63,6 +64,16 @@ public void testRedisTemplate() { Assertions.assertEquals("map2", value); } + @Configuration + @Import({AzureJedisPasswordlessAutoConfiguration.class, RedisAutoConfiguration.class}) + static class AzureRedisPasswordlessUTConfig { + + @Bean(name = "azureRedisCredentialSupplier") + Supplier redisCredential() { + return () -> REDIS_PASSWORD; + } + } + } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java index cb54ba7b8dc9..9b2b13ed26e7 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java @@ -21,7 +21,6 @@ import java.util.Optional; import java.util.function.Supplier; -import static org.mockito.Answers.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.when; @@ -184,7 +183,8 @@ void testGetPoolConfig() { when(clientConfiguration.getPoolConfig()).thenReturn(Optional.of(poolConfig)); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertEquals(poolConfig, azureJedisConnectionFactory.getPoolConfig()); + JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", azureJedisConnectionFactory); + Assertions.assertEquals(poolConfig, jedisClientConfiguration.getPoolConfig().get()); } @Test @@ -219,20 +219,9 @@ void testGetDatabase() { standaloneConfig = mock(RedisStandaloneConfiguration.class); when(standaloneConfig.getDatabase()).thenReturn(7); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertEquals(7, azureJedisConnectionFactory.getDatabase()); - } - - @Test - void testGetClientConfiguration() { - AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - - JedisClientConfiguration configuration = azureJedisConnectionFactory.getClientConfiguration(); - Assertions.assertNull(configuration); - - clientConfiguration = mock(JedisClientConfiguration.class); - azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertTrue(clientConfiguration == azureJedisConnectionFactory.getClientConfiguration()); + RedisStandaloneConfiguration cf = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", azureJedisConnectionFactory); + Assertions.assertEquals(7, cf.getDatabase()); } @Test @@ -246,13 +235,6 @@ void testSetConvertPipelineAndTxResults() { Assertions.assertTrue(azureJedisConnectionFactory.getConvertPipelineAndTxResults()); } - @Test - void testGetPort() { - standaloneConfig = mock(RedisStandaloneConfiguration.class); - when(standaloneConfig.getPort()).thenReturn(1717); - AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertEquals(1717, azureJedisConnectionFactory.getPort()); - } @Test void testIsUseSsl() { @@ -260,11 +242,15 @@ void testIsUseSsl() { when(clientConfiguration.isUseSsl()).thenReturn(true); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertEquals(true, azureJedisConnectionFactory.isUseSsl()); + JedisClientConfiguration cf = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", azureJedisConnectionFactory); + + Assertions.assertEquals(true, cf.isUseSsl()); when(clientConfiguration.isUseSsl()).thenReturn(false); azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertEquals(false, azureJedisConnectionFactory.isUseSsl()); + cf = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", azureJedisConnectionFactory); + + Assertions.assertEquals(false, cf.isUseSsl()); } @Test @@ -272,26 +258,9 @@ void testGetHostName() { standaloneConfig = mock(RedisStandaloneConfiguration.class); when(standaloneConfig.getHostName()).thenReturn("fake-host-name"); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertEquals("fake-host-name", azureJedisConnectionFactory.getHostName()); - } - - @Test - void testGetPasswordFromConfig() { - standaloneConfig = mock(RedisStandaloneConfiguration.class); - when(standaloneConfig.getPassword()).thenReturn(RedisPassword.of("password-from-config")); - AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertEquals("password-from-config", azureJedisConnectionFactory.getPassword()); - } + RedisStandaloneConfiguration cf = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", azureJedisConnectionFactory); - @Test - @SuppressWarnings("unchecked") - void testGetPasswordFromSupplier() { - standaloneConfig = mock(RedisStandaloneConfiguration.class, RETURNS_DEEP_STUBS); - when(standaloneConfig.getPassword().isPresent()).thenReturn(false); - credentialSupplier = mock(Supplier.class); - when(credentialSupplier.get()).thenReturn("password-from-credential-supplier"); - AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertEquals("password-from-credential-supplier", azureJedisConnectionFactory.getPassword()); + Assertions.assertEquals("fake-host-name", cf.getHostName()); } @Test @@ -299,7 +268,9 @@ void testGetClientName() { clientConfiguration = mock(JedisClientConfiguration.class); when(clientConfiguration.getClientName()).thenReturn(Optional.of("fake-clientName")); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertEquals("fake-clientName", azureJedisConnectionFactory.getClientName()); + JedisClientConfiguration jedisClientConfig = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", azureJedisConnectionFactory); + + Assertions.assertEquals("fake-clientName", jedisClientConfig.getClientName().get()); } @Test @@ -307,6 +278,5 @@ void testGetTimeout() { clientConfiguration = mock(JedisClientConfiguration.class); when(clientConfiguration.getReadTimeout()).thenReturn(Duration.ofSeconds(23)); AzureJedisConnectionFactory azureJedisConnectionFactory = new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, credentialSupplier); - Assertions.assertEquals(23000, azureJedisConnectionFactory.getTimeout()); } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java similarity index 59% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java index 98ed80ad7a80..e5b6a5ef60b2 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/passwordless/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.redis.passwordless; +package com.azure.spring.cloud.autoconfigure.redis; import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis.AzureJedisConnectionFactory; +import com.azure.spring.cloud.core.implementation.util.ReflectionUtils; import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -13,8 +14,11 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import redis.clients.jedis.JedisClientConfig; import java.time.Duration; import java.util.function.Supplier; @@ -88,10 +92,12 @@ void testGetPasswordFromSupplier() { .withBean("azureRedisCredentialSupplier", Supplier.class, () -> mockCredentialSupplier, beanDefinition -> beanDefinition.setPrimary(true)) .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getDatabase()).isEqualTo(1); - assertThat(cf.getPassword()).isEqualTo("fake-password-from-mock-supplier"); - assertThat(cf.isUseSsl()).isTrue(); + RedisStandaloneConfiguration redisStandaloneConfiguration = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); + JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", cf); + + + assertThat(redisStandaloneConfiguration.getDatabase()).isEqualTo(1); + assertThat(jedisClientConfiguration.isUseSsl()).isTrue(); }); } @@ -100,7 +106,9 @@ void testUseSsl() { this.contextRunner .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.isUseSsl()).isTrue(); + JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", cf); + + assertThat(jedisClientConfiguration.isUseSsl()).isTrue(); }); } @@ -110,10 +118,12 @@ void testRedisUrlConfiguration() { .withPropertyValues("spring.redis.host:foo", "spring.redis.url:redis://user:password@example:33") .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.isUseSsl()).isTrue(); + RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); + JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", cf); + + assertThat(standaloneConfig.getHostName()).isEqualTo("example"); + assertThat(standaloneConfig.getPort()).isEqualTo(33); + assertThat(jedisClientConfiguration.isUseSsl()).isTrue(); }); } @@ -124,10 +134,12 @@ void testOverrideUrlRedisConfiguration() { "spring.redis.ssl:false", "spring.redis.url:rediss://user:password@example:33") .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(cf.getPassword()).isEqualTo("password"); - assertThat(cf.isUseSsl()).isTrue(); + RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); + JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", cf); + + assertThat(standaloneConfig.getHostName()).isEqualTo("example"); + assertThat(standaloneConfig.getPort()).isEqualTo(33); + assertThat(jedisClientConfiguration.isUseSsl()).isTrue(); }); } @@ -135,9 +147,10 @@ void testOverrideUrlRedisConfiguration() { void testPasswordInUrlWithColon() { this.contextRunner.withPropertyValues("spring.redis.url:redis://:pass:word@example:33").run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(cf.getPassword()).isEqualTo("pass:word"); + RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); + + assertThat(standaloneConfig.getHostName()).isEqualTo("example"); + assertThat(standaloneConfig.getPort()).isEqualTo(33); }); } @@ -145,9 +158,10 @@ void testPasswordInUrlWithColon() { void testPasswordInUrlStartsWithColon() { this.contextRunner.withPropertyValues("spring.redis.url:redis://user::pass:word@example:33").run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("example"); - assertThat(cf.getPort()).isEqualTo(33); - assertThat(cf.getPassword()).isEqualTo(":pass:word"); + RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); + + assertThat(standaloneConfig.getHostName()).isEqualTo("example"); + assertThat(standaloneConfig.getPort()).isEqualTo(33); }); } @@ -158,12 +172,15 @@ void testRedisConfigurationWithPool() { "spring.redis.jedis.pool.max-wait:2000", "spring.redis.jedis.pool.time-between-eviction-runs:30000") .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getPoolConfig().getMinIdle()).isEqualTo(1); - assertThat(cf.getPoolConfig().getMaxIdle()).isEqualTo(4); - assertThat(cf.getPoolConfig().getMaxTotal()).isEqualTo(16); - assertThat(cf.getPoolConfig().getMaxWaitMillis()).isEqualTo(Duration.ofSeconds(2).toMillis()); - assertThat(cf.getPoolConfig().getTimeBetweenEvictionRunsMillis()).isEqualTo(Duration.ofSeconds(30).toMillis()); + JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", cf); + RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); + + assertThat(standaloneConfig.getHostName()).isEqualTo("foo"); + assertThat(jedisClientConfiguration.getPoolConfig().get().getMinIdle()).isEqualTo(1); + assertThat(jedisClientConfiguration.getPoolConfig().get().getMaxIdle()).isEqualTo(4); + assertThat(jedisClientConfiguration.getPoolConfig().get().getMaxTotal()).isEqualTo(16); + assertThat(jedisClientConfiguration.getPoolConfig().get().getMaxWaitMillis()).isEqualTo(Duration.ofSeconds(2).toMillis()); + assertThat(jedisClientConfiguration.getPoolConfig().get().getTimeBetweenEvictionRunsMillis()).isEqualTo(Duration.ofSeconds(30).toMillis()); }); } @@ -172,8 +189,11 @@ void testRedisConfigurationDisabledPool() { this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.jedis.pool.enabled:false") .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getClientConfiguration().isUsePooling()).isEqualTo(false); + RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); + JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", cf); + + assertThat(standaloneConfig.getHostName()).isEqualTo("foo"); + assertThat(jedisClientConfiguration.isUsePooling()).isEqualTo(false); }); } @@ -183,9 +203,11 @@ void testRedisConfigurationWithTimeoutAndConnectTimeout() { "spring.redis.connect-timeout:1000") .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getTimeout()).isEqualTo(250); - assertThat(cf.getClientConfiguration().getConnectTimeout().toMillis()).isEqualTo(1000); + RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); + JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", cf); + + assertThat(standaloneConfig.getHostName()).isEqualTo("foo"); + assertThat(jedisClientConfiguration.getConnectTimeout().toMillis()).isEqualTo(1000); }); } @@ -194,9 +216,11 @@ void testRedisConfigurationWithDefaultTimeouts() { this.contextRunner.withPropertyValues("spring.redis.host:foo") .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getTimeout()).isEqualTo(2000); - assertThat(cf.getClientConfiguration().getConnectTimeout().toMillis()).isEqualTo(2000); + RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); + JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", cf); + + assertThat(standaloneConfig.getHostName()).isEqualTo("foo"); + assertThat(jedisClientConfiguration.getConnectTimeout().toMillis()).isEqualTo(2000); }); } @@ -205,8 +229,11 @@ void testRedisConfigurationWithClientName() { this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.client-name:spring-boot") .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); - assertThat(cf.getHostName()).isEqualTo("foo"); - assertThat(cf.getClientName()).isEqualTo("spring-boot"); + JedisClientConfig jedisClientConfig = (JedisClientConfig) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "jedisClientConfig", cf); + RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); + + assertThat(standaloneConfig.getHostName()).isEqualTo("foo"); + assertThat(jedisClientConfig.getClientName()).isEqualTo("spring-boot"); }); } diff --git a/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-redis.yml b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml similarity index 56% rename from sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-redis.yml rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml index e71b97f3e6c7..79db51624bb3 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-redis.yml +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml @@ -1,6 +1,7 @@ spring: - redis: - username: redis-test-container-host + redis: host: redis-test-container-host azure: passwordless-enabled: true + main: + allow-bean-definition-overriding: true diff --git a/sdk/spring/spring-cloud-azure-integration-tests/pom.xml b/sdk/spring/spring-cloud-azure-integration-tests/pom.xml index 26c0f75e4c70..2178f7568447 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/pom.xml +++ b/sdk/spring/spring-cloud-azure-integration-tests/pom.xml @@ -125,29 +125,6 @@ test - - - org.testcontainers - testcontainers - 1.17.6 - test - - - - org.testcontainers - junit-jupiter - 1.17.6 - test - - - - - org.springframework.data - spring-data-redis - 2.7.6 - test - - From 6059b6c0bb3ecf28f5ee8ba37ed62caaf8a22d63 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 11 Jan 2023 13:53:03 +0800 Subject: [PATCH 28/38] fix tests failures --- .../redis/AzureJedisPasswordlessAutoConfigurationTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java index e5b6a5ef60b2..7ae31f3b80be 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -95,9 +95,8 @@ void testGetPasswordFromSupplier() { RedisStandaloneConfiguration redisStandaloneConfiguration = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", cf); - assertThat(redisStandaloneConfiguration.getDatabase()).isEqualTo(1); - assertThat(jedisClientConfiguration.isUseSsl()).isTrue(); + assertThat(jedisClientConfiguration.isUseSsl()).isFalse(); }); } @@ -108,14 +107,14 @@ void testUseSsl() { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); JedisClientConfiguration jedisClientConfiguration = (JedisClientConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "clientConfiguration", cf); - assertThat(jedisClientConfiguration.isUseSsl()).isTrue(); + assertThat(jedisClientConfiguration.isUseSsl()).isFalse(); }); } @Test void testRedisUrlConfiguration() { this.contextRunner - .withPropertyValues("spring.redis.host:foo", "spring.redis.url:redis://user:password@example:33") + .withPropertyValues("spring.redis.host:foo","spring.redis.ssl:true", "spring.redis.url:redis://user:password@example:33") .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); From 37b98720beb2b7eee2e07bbdc25da0dac038e061 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 11 Jan 2023 14:38:57 +0800 Subject: [PATCH 29/38] fix pipeline errors --- .../passwordless/jedis/AzureJedisConnectionFactory.java | 5 ----- .../redis/AzureJedisPasswordlessAutoConfigurationTest.java | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java index 927ce0e393a6..83d806000d20 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java @@ -12,7 +12,6 @@ import org.springframework.dao.DataAccessException; import org.springframework.data.redis.RedisConnectionFailureException; 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.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisPassword; @@ -218,10 +217,6 @@ private JedisClientConfig createClientConfig(RedisStandaloneConfiguration standa return builder.build(); } - private RedisPassword getRedisPassword() { - return RedisConfiguration.getPasswordOrElse(this.standaloneConfig, standaloneConfig::getPassword); - } - private void assertInitialized() { Assert.state(this.initialized, "JedisConnectionFactory was not initialized through afterPropertiesSet()"); Assert.state(!this.destroyed, "JedisConnectionFactory was destroyed and cannot be used anymore"); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java index 7ae31f3b80be..9669da670947 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -114,7 +114,7 @@ void testUseSsl() { @Test void testRedisUrlConfiguration() { this.contextRunner - .withPropertyValues("spring.redis.host:foo","spring.redis.ssl:true", "spring.redis.url:redis://user:password@example:33") + .withPropertyValues("spring.redis.host:foo", "spring.redis.ssl:true", "spring.redis.url:redis://user:password@example:33") .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); RedisStandaloneConfiguration standaloneConfig = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); From ffcc52231ab5f69a9df30fdddb934c4b69208883 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 11 Jan 2023 16:44:40 +0800 Subject: [PATCH 30/38] format yml --- .../src/test/resources/application-redis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml index 79db51624bb3..a4d0e30a3a01 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml @@ -4,4 +4,4 @@ spring: azure: passwordless-enabled: true main: - allow-bean-definition-overriding: true + allow-bean-definition-overriding: true From 41efadfc27e6c2b0786c537c49587a58a64e79e9 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 11 Jan 2023 17:11:31 +0800 Subject: [PATCH 31/38] refactor code --- .../implementation/redis/AzureRedisPasswordlessUT.java | 9 +++++++++ .../src/test/resources/application-redis.yml | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java index c2cbf94b71a2..bf983762909c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java @@ -67,6 +67,15 @@ public void testRedisTemplate() { @Configuration @Import({AzureJedisPasswordlessAutoConfiguration.class, RedisAutoConfiguration.class}) static class AzureRedisPasswordlessUTConfig { + Supplier redisCredential; + + public AzureRedisPasswordlessUTConfig(Supplier redisCredential) { + this.redisCredential = redisCredential; + } + } + + @Configuration + static class SupplierConfig { @Bean(name = "azureRedisCredentialSupplier") Supplier redisCredential() { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml index a4d0e30a3a01..c3f7ec8aee81 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml @@ -3,5 +3,4 @@ spring: host: redis-test-container-host azure: passwordless-enabled: true - main: - allow-bean-definition-overriding: true + From 0331e2ae0d3ddb764b7fb769c81fd6af0fcae0a5 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 11 Jan 2023 18:41:54 +0800 Subject: [PATCH 32/38] fix pipeline errors --- .../implementation/redis/AzureRedisPasswordlessUT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java index bf983762909c..da2a2ea3cc47 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java @@ -69,7 +69,7 @@ public void testRedisTemplate() { static class AzureRedisPasswordlessUTConfig { Supplier redisCredential; - public AzureRedisPasswordlessUTConfig(Supplier redisCredential) { + AzureRedisPasswordlessUTConfig(Supplier redisCredential) { this.redisCredential = redisCredential; } } From cb7db929eb8fc622f27424861aaaca5f4d679caf Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 16 Jan 2023 20:30:59 +0800 Subject: [PATCH 33/38] code refactor --- .../jedis/AzureJedisConnection.java | 2 +- .../jedis/AzureJedisConnectionFactory.java | 2 +- .../jedis/AzureRedisCredentialSupplier.java | 2 +- ...ureJedisPasswordlessAutoConfiguration.java | 6 ++-- .../redis/AzureJedisPasswordlessUtil.java | 3 ++ .../redis/AzureRedisPasswordlessUT.java | 3 +- .../AzureJedisConnectionFactoryTest.java | 3 +- .../AzureRedisCredentialSupplierTest.java | 3 +- ...edisPasswordlessAutoConfigurationTest.java | 28 +------------------ .../src/test/resources/application-redis.yml | 6 ---- .../AzureRedisPasswordlessPropertiesTest.java | 4 +++ 11 files changed, 19 insertions(+), 43 deletions(-) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/{ => data}/jedis/AzureJedisConnection.java (96%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/{ => data}/jedis/AzureJedisConnectionFactory.java (99%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/{ => data}/jedis/AzureRedisCredentialSupplier.java (97%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/{ => data}/jedis/AzureJedisConnectionFactoryTest.java (98%) rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/{ => data}/jedis/AzureRedisCredentialSupplierTest.java (84%) delete mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnection.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnection.java similarity index 96% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnection.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnection.java index 99f6d8583cf5..a2bd0cd77af9 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnection.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnection.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis; import org.springframework.data.redis.connection.jedis.JedisConnection; import org.springframework.lang.Nullable; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactory.java similarity index 99% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactory.java index 83d806000d20..ffb5e40f7ee0 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactory.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis; import com.azure.spring.cloud.service.implementation.redis.AzureJedisClientConfig; import com.azure.spring.cloud.service.implementation.redis.AzureJedisPool; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplier.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisCredentialSupplier.java similarity index 97% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplier.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisCredentialSupplier.java index ec1e62c2636f..0a1fb119112c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplier.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisCredentialSupplier.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis; import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java index e2ce80695c12..9eb0ea8a5360 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java @@ -4,8 +4,8 @@ package com.azure.spring.cloud.autoconfigure.redis; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; -import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis.AzureJedisConnectionFactory; -import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis.AzureRedisCredentialSupplier; +import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureJedisConnectionFactory; +import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureRedisCredentialSupplier; import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; import org.apache.commons.pool2.impl.GenericObjectPool; import org.springframework.beans.factory.ObjectProvider; @@ -56,7 +56,7 @@ AzureRedisPasswordlessProperties redisPasswordlessProperties() { @Bean(name = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) @ConditionalOnMissingBean(name = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) - Supplier azureRedisCredentialSupplier(ObjectProvider azureGlobalProperties, AzureRedisPasswordlessProperties azureRedisPasswordlessProperties) { + AzureRedisCredentialSupplier azureRedisCredentialSupplier(ObjectProvider azureGlobalProperties, AzureRedisPasswordlessProperties azureRedisPasswordlessProperties) { Properties properties = mergeAzureProperties(azureGlobalProperties.getIfAvailable(), azureRedisPasswordlessProperties).toProperties(); return new AzureRedisCredentialSupplier(properties); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java index 38df3d77ac3b..ff480c13f0a9 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java @@ -29,6 +29,9 @@ final class AzureJedisPasswordlessUtil { private static final boolean COMMONS_POOL2_AVAILABLE = ClassUtils.isPresent("org.apache.commons.pool2.ObjectPool", AzureJedisPasswordlessAutoConfiguration.class.getClassLoader()); + private AzureJedisPasswordlessUtil() { + } + static AzureRedisPasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureRedisPasswordlessProperties redisPasswordlessProperties) { AzureRedisPasswordlessProperties target = new AzureRedisPasswordlessProperties(); copyPropertiesIgnoreNull(redisPasswordlessProperties.getScopes(), target.getScopes()); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java index da2a2ea3cc47..b9e87724030a 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java @@ -15,7 +15,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.containers.GenericContainer; @@ -29,7 +28,7 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @Testcontainers -@ActiveProfiles("redis") +//@ActiveProfiles("redis") @DisabledOnOs({OS.WINDOWS, OS.MAC}) public class AzureRedisPasswordlessUT { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java similarity index 98% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java index 9b2b13ed26e7..5035424ca258 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureJedisConnectionFactoryTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis; +import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureJedisConnectionFactory; import com.azure.spring.cloud.core.implementation.util.ReflectionUtils; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.Assertions; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisCredentialSupplierTest.java similarity index 84% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisCredentialSupplierTest.java index 6e3c53ce9109..4cce269047ca 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/jedis/AzureRedisCredentialSupplierTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisCredentialSupplierTest.java @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis; import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; +import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureRedisCredentialSupplier; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java index 9669da670947..dfc6c521880f 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -3,12 +3,11 @@ package com.azure.spring.cloud.autoconfigure.redis; -import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.jedis.AzureJedisConnectionFactory; +import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureJedisConnectionFactory; import com.azure.spring.cloud.core.implementation.util.ReflectionUtils; import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.data.redis.JedisClientConfigurationBuilderCustomizer; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -17,7 +16,6 @@ import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import redis.clients.jedis.JedisClientConfig; import java.time.Duration; @@ -246,28 +244,4 @@ JedisClientConfigurationBuilderCustomizer customizer() { } - @Configuration(proxyBeanMethods = false) - static class JedisConnectionFactoryCaptorConfiguration { - - @Bean - JedisConnectionFactoryCaptor jedisConnectionFactoryCaptor() { - return new JedisConnectionFactoryCaptor(); - } - - } - - static class JedisConnectionFactoryCaptor implements BeanPostProcessor { - - static JedisConnectionFactory connectionFactory; - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) { - if (bean instanceof JedisConnectionFactory) { - connectionFactory = (JedisConnectionFactory) bean; - } - return bean; - } - - } - } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml deleted file mode 100644 index c3f7ec8aee81..000000000000 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/resources/application-redis.yml +++ /dev/null @@ -1,6 +0,0 @@ -spring: - redis: - host: redis-test-container-host - azure: - passwordless-enabled: true - diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java index 645f4cb5bac3..30c5f85c3635 100644 --- a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java @@ -24,6 +24,10 @@ void testGetScopes() { AzureProfileProperties profile = new AzureProfileProperties(); AzureRedisPasswordlessProperties properties = new AzureRedisPasswordlessProperties(); + properties.setScopes("fake-scopes"); + Assertions.assertEquals("fake-scopes", properties.getScopes()); + + properties.setScopes(null); String scopes = properties.getScopes(); Assertions.assertEquals(REDIS_SCOPE_GLOBAL, scopes); From a4bef7c456b83b555b715daeb9f01c2c44725481 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Mon, 16 Jan 2023 20:44:55 +0800 Subject: [PATCH 34/38] fix checkStyle errors --- .../src/main/resources/checkstyle/checkstyle-suppressions.xml | 3 +++ .../data/jedis/AzureJedisConnectionFactoryTest.java | 1 - .../data/jedis/AzureRedisCredentialSupplierTest.java | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml index 071d5ce7f731..a382f4f8a8da 100755 --- a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml +++ b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml @@ -127,6 +127,9 @@ the main ServiceBusClientBuilder. --> + + + diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java index 5035424ca258..943a554dc428 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java @@ -3,7 +3,6 @@ package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis; -import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureJedisConnectionFactory; import com.azure.spring.cloud.core.implementation.util.ReflectionUtils; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.Assertions; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisCredentialSupplierTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisCredentialSupplierTest.java index 4cce269047ca..0b71a27f80df 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisCredentialSupplierTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisCredentialSupplierTest.java @@ -4,7 +4,6 @@ package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis; import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; -import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureRedisCredentialSupplier; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; From e9dc7575ddc5195191300a162a2f9723d2fcfd22 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 17 Jan 2023 11:39:03 +0800 Subject: [PATCH 35/38] address comments --- ...sswordlessUT.java => AzureRedisPasswordlessTest.java} | 9 ++++----- .../data/jedis/AzureJedisConnectionFactoryTest.java | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/{AzureRedisPasswordlessUT.java => AzureRedisPasswordlessTest.java} (92%) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessTest.java similarity index 92% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessTest.java index b9e87724030a..56f03e644ed7 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessUT.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessTest.java @@ -28,9 +28,8 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @Testcontainers -//@ActiveProfiles("redis") @DisabledOnOs({OS.WINDOWS, OS.MAC}) -public class AzureRedisPasswordlessUT { +public class AzureRedisPasswordlessTest { @Autowired private RedisTemplate redisTemplate; @@ -53,7 +52,7 @@ static void redisProperties(DynamicPropertyRegistry registry) { } @Test - public void testRedisTemplate() { + void testRedisTemplate() { Map valueMap = new HashMap<>(); valueMap.put("valueMap1", "map1"); valueMap.put("valueMap2", "map2"); @@ -65,10 +64,10 @@ public void testRedisTemplate() { @Configuration @Import({AzureJedisPasswordlessAutoConfiguration.class, RedisAutoConfiguration.class}) - static class AzureRedisPasswordlessUTConfig { + static class AzureRedisPasswordlessTestConfig { Supplier redisCredential; - AzureRedisPasswordlessUTConfig(Supplier redisCredential) { + AzureRedisPasswordlessTestConfig(Supplier redisCredential) { this.redisCredential = redisCredential; } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java index 943a554dc428..da81d1cd6f1a 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureJedisConnectionFactoryTest.java @@ -27,9 +27,9 @@ class AzureJedisConnectionFactoryTest { - RedisStandaloneConfiguration standaloneConfig; - JedisClientConfiguration clientConfiguration; - Supplier credentialSupplier; + private RedisStandaloneConfiguration standaloneConfig; + private JedisClientConfiguration clientConfiguration; + private Supplier credentialSupplier; @Test void testFetchJedisConnectorWithNoPool() { From c538c7a5b36c4009d6c4394b769ead2e116b574e Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 17 Jan 2023 17:57:43 +0800 Subject: [PATCH 36/38] address comments --- ...ureJedisPasswordlessAutoConfiguration.java | 16 +++----- ...isAutoConfigurationTestContainerTest.java} | 37 ++++++++++++++----- ...edisPasswordlessAutoConfigurationTest.java | 22 +++++------ 3 files changed, 44 insertions(+), 31 deletions(-) rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/{AzureRedisPasswordlessTest.java => passwordless/data/jedis/AzureRedisAutoConfigurationTestContainerTest.java} (71%) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java index 9eb0ea8a5360..c3d1f2f6c36e 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java @@ -8,8 +8,6 @@ import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureRedisCredentialSupplier; import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; import org.apache.commons.pool2.impl.GenericObjectPool; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; @@ -27,7 +25,6 @@ import redis.clients.jedis.Jedis; import java.util.Properties; -import java.util.function.Supplier; import static com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessUtil.getJedisClientConfiguration; import static com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessUtil.getStandaloneConfig; @@ -46,23 +43,22 @@ @EnableConfigurationProperties(RedisProperties.class) public class AzureJedisPasswordlessAutoConfiguration { - private static final String AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME = "azureRedisCredentialSupplier"; - @Bean @ConfigurationProperties(prefix = "spring.redis.azure") AzureRedisPasswordlessProperties redisPasswordlessProperties() { return new AzureRedisPasswordlessProperties(); } - @Bean(name = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) - @ConditionalOnMissingBean(name = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) - AzureRedisCredentialSupplier azureRedisCredentialSupplier(ObjectProvider azureGlobalProperties, AzureRedisPasswordlessProperties azureRedisPasswordlessProperties) { - Properties properties = mergeAzureProperties(azureGlobalProperties.getIfAvailable(), azureRedisPasswordlessProperties).toProperties(); + @Bean + @ConditionalOnMissingBean + AzureRedisCredentialSupplier azureRedisCredentialSupplier(AzureGlobalProperties azureGlobalProperties, AzureRedisPasswordlessProperties azureRedisPasswordlessProperties) { + Properties properties = mergeAzureProperties(azureGlobalProperties, azureRedisPasswordlessProperties).toProperties(); return new AzureRedisCredentialSupplier(properties); } @Bean - AzureJedisConnectionFactory azureRedisConnectionFactory(RedisProperties redisProperties, @Qualifier(value = AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME) Supplier azureRedisCredentialSupplier) { + @ConditionalOnMissingBean + AzureJedisConnectionFactory azureRedisConnectionFactory(RedisProperties redisProperties, AzureRedisCredentialSupplier azureRedisCredentialSupplier) { RedisStandaloneConfiguration standaloneConfig = getStandaloneConfig(redisProperties); JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(redisProperties); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisAutoConfigurationTestContainerTest.java similarity index 71% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessTest.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisAutoConfigurationTestContainerTest.java index 56f03e644ed7..1ece227b0d6e 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/AzureRedisPasswordlessTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisAutoConfigurationTestContainerTest.java @@ -1,10 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.implementation.redis; +package com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis; +import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; import com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessAutoConfiguration; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; @@ -24,12 +26,14 @@ import java.util.HashMap; import java.util.Map; -import java.util.function.Supplier; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @Testcontainers @DisabledOnOs({OS.WINDOWS, OS.MAC}) -public class AzureRedisPasswordlessTest { +public class AzureRedisAutoConfigurationTestContainerTest { @Autowired private RedisTemplate redisTemplate; @@ -52,7 +56,10 @@ static void redisProperties(DynamicPropertyRegistry registry) { } @Test - void testRedisTemplate() { + @Order(1) + void testSetAndGet() { + redis.start(); + Map valueMap = new HashMap<>(); valueMap.put("valueMap1", "map1"); valueMap.put("valueMap2", "map2"); @@ -62,22 +69,32 @@ void testRedisTemplate() { Assertions.assertEquals("map2", value); } + @Test + @Order(2) + void testGetVauleAfterSet() { + String value = (String) redisTemplate.opsForValue().get("valueMap3"); + Assertions.assertEquals("map3", value); + } + + @Configuration @Import({AzureJedisPasswordlessAutoConfiguration.class, RedisAutoConfiguration.class}) static class AzureRedisPasswordlessTestConfig { - Supplier redisCredential; + AzureRedisCredentialSupplier azureRedisCredentialSupplier; - AzureRedisPasswordlessTestConfig(Supplier redisCredential) { - this.redisCredential = redisCredential; + AzureRedisPasswordlessTestConfig(AzureRedisCredentialSupplier azureRedisCredentialSupplier) { + this.azureRedisCredentialSupplier = azureRedisCredentialSupplier; } } @Configuration static class SupplierConfig { - @Bean(name = "azureRedisCredentialSupplier") - Supplier redisCredential() { - return () -> REDIS_PASSWORD; + @Bean + AzureRedisCredentialSupplier azureRedisCredentialSupplier() { + AzureAuthenticationTemplate template = mock(AzureAuthenticationTemplate.class); + when(template.getTokenAsPassword()).thenReturn(REDIS_PASSWORD); + return new AzureRedisCredentialSupplier(template); } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java index dfc6c521880f..355056b7544f 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java @@ -3,31 +3,31 @@ package com.azure.spring.cloud.autoconfigure.redis; +import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureJedisConnectionFactory; +import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureRedisCredentialSupplier; import com.azure.spring.cloud.core.implementation.util.ReflectionUtils; import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.data.redis.JedisClientConfigurationBuilderCustomizer; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; -import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder; import redis.clients.jedis.JedisClientConfig; import java.time.Duration; -import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Tests for {@link AzureJedisPasswordlessAutoConfiguration}. */ class AzureJedisPasswordlessAutoConfigurationTest { - private static final String AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME = "azureRedisCredentialSupplier"; private static final String REDIS_SCOPE_GLOBAL = "https://*.cacheinfra.windows.net:10225/appid/.default"; private static final String REDIS_SCOPE_CHINA = "https://*.cacheinfra.windows.net.china:10225/appid/.default"; @@ -40,13 +40,13 @@ class AzureJedisPasswordlessAutoConfigurationTest { "spring.redis.username = testuser", "spring.redis.host = testhost" ) - .withConfiguration(AutoConfigurations.of(AzureJedisPasswordlessAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(AzureJedisPasswordlessAutoConfiguration.class, CustomConfiguration.class)); @Test @SuppressWarnings("unchecked") void testCredentialSupplier() { this.contextRunner.run((context) -> { - Supplier supplier = (Supplier) context.getBean(AZURE_REDIS_CREDENTIAL_SUPPLIER_BEAN_NAME); + AzureRedisCredentialSupplier supplier = context.getBean(AzureRedisCredentialSupplier.class); Assertions.assertNotNull(supplier); }); } @@ -85,9 +85,10 @@ void connectionFactoryDefaultsToJedis() { @Test @SuppressWarnings("unchecked") void testGetPasswordFromSupplier() { - Supplier mockCredentialSupplier = () -> "fake-password-from-mock-supplier"; + AzureRedisCredentialSupplier azureRedisCredentialSupplier = mock(AzureRedisCredentialSupplier.class); + when(azureRedisCredentialSupplier.get()).thenReturn("fake-password-from-mock-supplier"); this.contextRunner.withPropertyValues("spring.redis.host:foo", "spring.redis.database:1") - .withBean("azureRedisCredentialSupplier", Supplier.class, () -> mockCredentialSupplier, beanDefinition -> beanDefinition.setPrimary(true)) + .withBean("azureRedisCredentialSupplier", AzureRedisCredentialSupplier.class, () -> azureRedisCredentialSupplier, beanDefinition -> beanDefinition.setPrimary(true)) .run((context) -> { AzureJedisConnectionFactory cf = context.getBean(AzureJedisConnectionFactory.class); RedisStandaloneConfiguration redisStandaloneConfiguration = (RedisStandaloneConfiguration) ReflectionUtils.getField(AzureJedisConnectionFactory.class, "standaloneConfig", cf); @@ -238,10 +239,9 @@ void testRedisConfigurationWithClientName() { static class CustomConfiguration { @Bean - JedisClientConfigurationBuilderCustomizer customizer() { - return JedisClientConfigurationBuilder::useSsl; + AzureGlobalProperties azureGlobalProperties() { + return new AzureGlobalProperties(); } - } } From e3235e08b9a2d46c3f3add716681dfa4182b4720 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 17 Jan 2023 18:05:03 +0800 Subject: [PATCH 37/38] Fix order issue in tests --- .../AzureRedisAutoConfigurationTestContainerTest.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisAutoConfigurationTestContainerTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisAutoConfigurationTestContainerTest.java index 1ece227b0d6e..aaa2e2b7bb38 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisAutoConfigurationTestContainerTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/redis/passwordless/data/jedis/AzureRedisAutoConfigurationTestContainerTest.java @@ -6,8 +6,10 @@ import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; import com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessAutoConfiguration; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; import org.springframework.beans.factory.annotation.Autowired; @@ -33,6 +35,7 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @Testcontainers @DisabledOnOs({OS.WINDOWS, OS.MAC}) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class AzureRedisAutoConfigurationTestContainerTest { @Autowired @@ -58,7 +61,6 @@ static void redisProperties(DynamicPropertyRegistry registry) { @Test @Order(1) void testSetAndGet() { - redis.start(); Map valueMap = new HashMap<>(); valueMap.put("valueMap1", "map1"); @@ -80,11 +82,7 @@ void testGetVauleAfterSet() { @Configuration @Import({AzureJedisPasswordlessAutoConfiguration.class, RedisAutoConfiguration.class}) static class AzureRedisPasswordlessTestConfig { - AzureRedisCredentialSupplier azureRedisCredentialSupplier; - AzureRedisPasswordlessTestConfig(AzureRedisCredentialSupplier azureRedisCredentialSupplier) { - this.azureRedisCredentialSupplier = azureRedisCredentialSupplier; - } } @Configuration From bdb5619b79f6ff955a1205fe461544842300a806 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 18 Jan 2023 11:16:18 +0800 Subject: [PATCH 38/38] code refactor --- .../AzureRedisPasswordlessProperties.java | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java index 5889d8e9da60..2658052ff1f1 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java @@ -4,7 +4,6 @@ package com.azure.spring.cloud.service.implementation.passwordless; import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; - import java.util.HashMap; import java.util.Map; @@ -13,12 +12,17 @@ */ public class AzureRedisPasswordlessProperties extends AzurePasswordlessProperties { + private static final String REDIS_SCOPE_AZURE = "https://*.cacheinfra.windows.net:10225/appid/.default"; + private static final String REDIS_SCOPE_AZURE_CHINA = "https://*.cacheinfra.windows.net.china:10225/appid/.default"; + private static final String REDIS_SCOPE_AZURE_GERMANY = "https://*.cacheinfra.windows.net.germany:10225/appid/.default"; + private static final String REDIS_SCOPE_AZURE_US_GOVERNMENT = "https://*.cacheinfra.windows.us.government.net:10225/appid/.default"; + private static final Map REDIS_SCOPE_MAP = new HashMap() { { - put(AzureProfileOptionsProvider.CloudType.AZURE, "https://*.cacheinfra.windows.net:10225/appid/.default"); - put(AzureProfileOptionsProvider.CloudType.AZURE_CHINA, "https://*.cacheinfra.windows.net.china:10225/appid/.default"); - put(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY, "https://*.cacheinfra.windows.net.germany:10225/appid/.default"); - put(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT, "https://*.cacheinfra.windows.us.government.net:10225/appid/.default"); + put(AzureProfileOptionsProvider.CloudType.AZURE, REDIS_SCOPE_AZURE); + put(AzureProfileOptionsProvider.CloudType.AZURE_CHINA, REDIS_SCOPE_AZURE_CHINA); + put(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY, REDIS_SCOPE_AZURE_GERMANY); + put(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT, REDIS_SCOPE_AZURE_US_GOVERNMENT); } }; @@ -28,18 +32,7 @@ public String getScopes() { } private String getRedisScopes() { - String redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE); - AzureProfileOptionsProvider.CloudType cloudType = getProfile().getCloudType(); - if (AzureProfileOptionsProvider.CloudType.AZURE.equals(cloudType)) { - redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE); - } else if (AzureProfileOptionsProvider.CloudType.AZURE_CHINA.equals(cloudType)) { - redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE_CHINA); - } else if (AzureProfileOptionsProvider.CloudType.AZURE_GERMANY.equals(cloudType)) { - redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE_GERMANY); - } else if (AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT.equals(cloudType)) { - redisScope = REDIS_SCOPE_MAP.get(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT); - } - return redisScope; + return REDIS_SCOPE_MAP.getOrDefault(getProfile().getCloudType(), REDIS_SCOPE_AZURE); } }