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 fdb124717655..ec1f52eaaf14 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 @@ -631,6 +631,7 @@ + diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index 4d971d9c074d..f9d89d18ea30 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -1,27 +1,5 @@ # Format; # :;dependency-version;current-version -# -# The dependency-version is used by other Azure SDKs outside of the release group when listing the library as a Maven -# dependency in its POM. The current-version is used by the library to denote its version and as the Maven dependency -# version for other Azure SDKs inside the same release group. -# -# Given com.azure:azure-example;1.1.0;1.2.0-beta.1 in the release group /sdk/example -# - com.azure:azure-example2 in release group /sdk/example will use the current-version (1.2.0-beta.1) when using -# com.azure:azure-example as a dependency. -# - com.azure:azure-not-an-example in release group /sdk/notanexample will use the dependency-version (1.1.0) when using -# com.azure:azure-example as a dependency. -# -# When releasing a library the current-version should be set to the version being released to Maven. Upon release of the -# library an automated PR will be submitted to update the dependency-version to the version that was released and the -# dependency-version to the next in development version. -# -# The next in development version is determined by the version that was released. -# - If a beta version was released the beta number is incremented. (1.2.0-beta.1 -> 1.2.0-beta.2) -# - If a GA version was released the current-version is the next minor beta. (1.2.0 -> 1.3.0-beta.1) -# -# When adding a new library the dependency-version and current-version will be the same until the first release is -# performed. So, if com.azure:azure-new-library is added with major version 2 it'll use -# com.azure:azure-new-library;2.0.0-beta.1;2.0.0-beta.1 as its initial version tag. com.azure:azure-sdk-all;1.0.0;1.0.0 com.azure:azure-sdk-parent;1.6.0;1.6.0 diff --git a/sdk/spring/azure-identity-spring-library/pom.xml b/sdk/spring/azure-identity-spring-library/pom.xml new file mode 100644 index 000000000000..1d38f50de519 --- /dev/null +++ b/sdk/spring/azure-identity-spring-library/pom.xml @@ -0,0 +1,69 @@ + + + + 4.0.0 + + com.azure.spring + azure-spring-boot-service + 1.0.0 + + azure-identity-spring-library + jar + Azure Identity Spring Integration Library + 1.0.0-beta.1 + + UTF-8 + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + + + + org.springframework + spring-context + 5.2.8.RELEASE + + + org.springframework + spring-core + 5.2.8.RELEASE + + + com.azure + azure-identity + 1.0.9 + + + org.junit.jupiter + junit-jupiter-api + 5.6.2 + test + + + org.junit.jupiter + junit-jupiter-params + 5.6.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.6.2 + test + + + junit + junit + test + 4.13 + + + diff --git a/sdk/spring/azure-identity-spring-library/src/main/java/com/microsoft/azure/identity/spring/SpringEnvironmentTokenBuilder.java b/sdk/spring/azure-identity-spring-library/src/main/java/com/microsoft/azure/identity/spring/SpringEnvironmentTokenBuilder.java new file mode 100644 index 000000000000..48702ab16f85 --- /dev/null +++ b/sdk/spring/azure-identity-spring-library/src/main/java/com/microsoft/azure/identity/spring/SpringEnvironmentTokenBuilder.java @@ -0,0 +1,198 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.identity.spring; + +import java.util.HashMap; +import java.util.Optional; + +import org.springframework.core.env.Environment; + +import com.azure.core.credential.TokenCredential; +import com.azure.identity.ChainedTokenCredential; +import com.azure.identity.ChainedTokenCredentialBuilder; +import com.azure.identity.ClientCertificateCredentialBuilder; +import com.azure.identity.ClientSecretCredentialBuilder; +import com.azure.identity.DefaultAzureCredentialBuilder; + +/** + * A helper class to deal with credentials in a Spring environment. + * + *

+ * This helper class makes it possible to configure credentials to be used + * within a Spring context. + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Property TuplesDescription
azure.credential.(name.)tenantId
+ * azure.credential.(name.)clientId
+ * azure.credential.(name.)clientSecret
the Azure Tenant ID
+ * the Client ID
+ * the Client Certificate
+ *
azure.credential.(name.)tenantId
+ * azure.credential.(name.)clientId
+ * azure.credential.(name.)clientCertificate
the Azure Tenant ID
+ * the Client ID
+ * the path to the PEM client certificate
+ * + * where name is the name of the credential. Note if + * name is entirely omitted it is taken to be the default + * credential. Note if the default credential is omitted it is configure to use + * AzureDefaultCredential which allows for the use a Managed Identity (if it is + * present). + * + * @author manfred.riem@microsoft.com + */ +public class SpringEnvironmentTokenBuilder { + + /** + * Defines the AZURE_CREDENTIAL_PREFIX. + */ + private static final String AZURE_CREDENTIAL_PREFIX = "azure.credential."; + + /** + * Stores the named credentials. + */ + private final HashMap credentials; + + /** + * Stores the name of the credential to be returned. If omitted, the default + * credential will be returned. + */ + private String name = ""; + + /** + * Constructor. + */ + public SpringEnvironmentTokenBuilder() { + credentials = new HashMap<>(); + credentials.put("", new DefaultAzureCredentialBuilder().build()); + } + + /** + * Populate from Environment. + * + * @param environment the environment. + */ + public SpringEnvironmentTokenBuilder fromEnvironment(Environment environment) { + populateNamedCredential(environment, ""); + String credentialNamesKey = AZURE_CREDENTIAL_PREFIX + "names"; + if (environment.containsProperty(credentialNamesKey)) { + String[] credentialNames = environment.getProperty(credentialNamesKey).split(","); + for (int i = 0; i < credentialNames.length; i++) { + populateNamedCredential(environment, credentialNames[i]); + } + } + return this; + } + + /** + * Sets a credential to override a named credential. If this credential fails to produce a token, + * the original token credential will be used. + * + * @param name + * @param credential + * @return + */ + public SpringEnvironmentTokenBuilder overrideNamedCredential(String name, TokenCredential credential) { + TokenCredential currentCredential = credentials.get(name); + if (currentCredential == null) { + credentials.put(name, credential); + } else { + ChainedTokenCredentialBuilder builder = new ChainedTokenCredentialBuilder(); + builder.addFirst(credential); + builder.addLast(currentCredential); + credentials.put(name, builder.build()); + } + + return this; + } + + /** + * Populate a named credential. + * + * @param environment the environment + * @param name the name. + */ + private void populateNamedCredential(Environment environment, String name) { + String standardizedName = name; + + if (!standardizedName.equals("") && !standardizedName.endsWith(".")) { + standardizedName = standardizedName + "."; + } + + String tenantIdKey = AZURE_CREDENTIAL_PREFIX + standardizedName + "tenantId"; + String clientIdKey = AZURE_CREDENTIAL_PREFIX + standardizedName + "clientId"; + String clientSecretKey = AZURE_CREDENTIAL_PREFIX + standardizedName + "clientSecret"; + + String tenantId = environment.getProperty(tenantIdKey); + String clientId = environment.getProperty(clientIdKey); + String clientSecret = environment.getProperty(clientSecretKey); + + if (tenantId != null && clientId != null && clientSecret != null) { + TokenCredential credential = new ClientSecretCredentialBuilder().tenantId(tenantId).clientId(clientId) + .clientSecret(clientSecret).build(); + credentials.put(name, credential); + return; + } + + String clientCertificateKey = AZURE_CREDENTIAL_PREFIX + standardizedName + "clientCertificate"; + String clientCertificatePath = environment.getProperty(clientCertificateKey); + + if (tenantId != null && clientId != null && clientCertificatePath != null) { + TokenCredential credential = new ClientCertificateCredentialBuilder().tenantId(tenantId).clientId(clientId) + .pemCertificate(clientCertificatePath).build(); + credentials.put(name, credential); + return; + } + + if (!name.equals("")) { + throw new IllegalStateException("Configuration for azure.credential." + name + " is incomplete"); + } + } + + /** + * Sets the builder to return a credential named name + * + * @param name + * @return + */ + public SpringEnvironmentTokenBuilder namedCredential(String name) { + this.name = name; + return this; + } + + /** + * Sets the builder to return the default credential. + */ + public SpringEnvironmentTokenBuilder defaultCredential() { + return namedCredential(""); + } + + /** + * Builds an Azure TokenCredendial. + * + * @throws IllegalArgumentException if attempting to retrieve a named credential + * not defined in the environment. + */ + public TokenCredential build() { + TokenCredential result = credentials.get(name); + if (result == null) { + throw new IllegalArgumentException( + "Attempting to retrieve Azure credential not configured in the environment. (name=" + name + ")"); + } else { + return result; + } + } +} diff --git a/sdk/spring/azure-identity-spring-library/src/test/java/com/microsoft/azure/identity/spring/SpringEnvironmentTokenBuilderTest.java b/sdk/spring/azure-identity-spring-library/src/test/java/com/microsoft/azure/identity/spring/SpringEnvironmentTokenBuilderTest.java new file mode 100644 index 000000000000..496385d04edf --- /dev/null +++ b/sdk/spring/azure-identity-spring-library/src/test/java/com/microsoft/azure/identity/spring/SpringEnvironmentTokenBuilderTest.java @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.microsoft.azure.identity.spring; + +import com.azure.identity.ClientSecretCredential; +import com.azure.identity.DefaultAzureCredential; +import com.azure.identity.DefaultAzureCredentialBuilder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import org.junit.jupiter.api.Test; +import org.springframework.core.env.StandardEnvironment; + +/** + * The unit tests for the AzureIdentitySpringHelper class. + * + * @author manfred.riem@microsoft.com + */ +public class SpringEnvironmentTokenBuilderTest { + + /** + * Test getDefaultCredential method. + */ + @Test + public void testGetDefaultCredential() { + SpringEnvironmentTokenBuilder builder = new SpringEnvironmentTokenBuilder(); + assertNotNull(builder.build()); + assertEquals(builder.build(), builder.defaultCredential().build()); + } + + /** + * Test populate method. + */ + @Test + public void testPopulate() { + System.setProperty("azure.credential.names", ""); + System.setProperty("azure.credential.tenantId", "tenantId"); + System.setProperty("azure.credential.clientId", "clientId"); + System.setProperty("azure.credential.clientSecret", "clientSecret"); + StandardEnvironment environment = new StandardEnvironment(); + SpringEnvironmentTokenBuilder builder = new SpringEnvironmentTokenBuilder(); + builder.fromEnvironment(environment); + + assertNotNull(builder.build()); + assertTrue(builder.build() instanceof ClientSecretCredential); + assertEquals(builder.build(), builder.defaultCredential().build()); + } + + /** + * Test populate method. + */ + @Test + public void testPopulate2() { + System.setProperty("azure.credential.names", "myname"); + System.setProperty("azure.credential.myname.tenantId", "tenantId"); + System.setProperty("azure.credential.myname.clientId", "clientId"); + System.setProperty("azure.credential.myname.clientSecret", "clientSecret"); + StandardEnvironment environment = new StandardEnvironment(); + SpringEnvironmentTokenBuilder builder = new SpringEnvironmentTokenBuilder(); + builder.fromEnvironment(environment); + assertNotNull(builder.namedCredential("myname").build()); + assertTrue(builder.build() instanceof ClientSecretCredential); + assertNotEquals(builder.build(), builder.defaultCredential().build()); + } + + /** + * Test populate method. + */ + @Test + public void testPopulate3() { + System.setProperty("azure.credential.names", "myname2"); + System.setProperty("azure.credential.myname2.tenantId", "tenantId"); + System.setProperty("azure.credential.myname2.clientSecret", "clientSecret"); + StandardEnvironment environment = new StandardEnvironment(); + SpringEnvironmentTokenBuilder builder = new SpringEnvironmentTokenBuilder(); + try { + builder.fromEnvironment(environment); + fail(); + } catch (Throwable t) { + assertEquals(IllegalStateException.class, t.getClass(), + "Unexpected exception class on missing configuration field."); + } + } +} diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-servicebus/pom.xml b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-servicebus/pom.xml index d2f7d689574b..c9ad24eef608 100644 --- a/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-servicebus/pom.xml +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-boot-sample-servicebus/pom.xml @@ -31,7 +31,7 @@ com.azure azure-messaging-servicebus - 7.0.0-beta.7 + 7.0.0-beta.6 diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-cloud-sample-eventhubs-binder/pom.xml b/sdk/spring/azure-spring-boot-samples/azure-spring-cloud-sample-eventhubs-binder/pom.xml index 573325fe468d..c1bb783ecc09 100644 --- a/sdk/spring/azure-spring-boot-samples/azure-spring-cloud-sample-eventhubs-binder/pom.xml +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-cloud-sample-eventhubs-binder/pom.xml @@ -36,6 +36,12 @@ azure-spring-cloud-stream-binder-eventhubs 2.0.0-beta.1 + + + com.azure.spring + azure-spring-cloud-storage + 2.0.0-beta.1 + org.springframework.boot spring-boot-starter-logging @@ -79,7 +85,7 @@ org.springframework.boot spring-boot-maven-plugin - + com.microsoft.azure azure-webapp-maven-plugin diff --git a/sdk/spring/azure-spring-boot-samples/azure-spring-cloud-sample-storage-resource/pom.xml b/sdk/spring/azure-spring-boot-samples/azure-spring-cloud-sample-storage-resource/pom.xml index 1060a9095a6c..f8738e17f6cc 100644 --- a/sdk/spring/azure-spring-boot-samples/azure-spring-cloud-sample-storage-resource/pom.xml +++ b/sdk/spring/azure-spring-boot-samples/azure-spring-cloud-sample-storage-resource/pom.xml @@ -76,4 +76,12 @@ test + + + + org.springframework.boot + spring-boot-maven-plugin + + + diff --git a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/cache/AzureRedisAutoConfiguration.java b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/cache/AzureRedisAutoConfiguration.java index b85ac7e56def..81d37b73ddb7 100644 --- a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/cache/AzureRedisAutoConfiguration.java +++ b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/cache/AzureRedisAutoConfiguration.java @@ -3,12 +3,13 @@ package com.azure.spring.cloud.autoconfigure.cache; -import com.microsoft.azure.management.redis.RedisCache; import com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; +import com.azure.spring.cloud.context.core.config.AzureProperties; +import com.azure.spring.cloud.context.core.impl.RedisCacheManager; import com.azure.spring.cloud.telemetry.TelemetryCollector; +import com.microsoft.azure.management.Azure; +import com.microsoft.azure.management.redis.RedisCache; import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -31,7 +32,6 @@ @AutoConfigureAfter(AzureContextAutoConfiguration.class) @ConditionalOnProperty(value = "spring.cloud.azure.redis.enabled", matchIfMissing = true) @ConditionalOnClass(RedisOperations.class) -@ConditionalOnBean(ResourceManagerProvider.class) @EnableConfigurationProperties(AzureRedisProperties.class) public class AzureRedisAutoConfiguration { private static final String REDIS = "Redis"; @@ -41,14 +41,20 @@ public void collectTelemetry() { TelemetryCollector.getInstance().addService(REDIS); } + @ConditionalOnMissingBean + @Bean + public RedisCacheManager redisCacheManager(Azure azure, AzureProperties azureProperties) { + return new RedisCacheManager(azure, azureProperties); + } + @ConditionalOnMissingBean @Primary @Bean - public RedisProperties redisProperties(ResourceManagerProvider resourceManagerProvider, - AzureRedisProperties azureRedisProperties) { + public RedisProperties redisProperties(AzureRedisProperties azureRedisProperties, + RedisCacheManager redisCacheManager) { String cacheName = azureRedisProperties.getName(); - RedisCache redisCache = resourceManagerProvider.getRedisCacheManager().getOrCreate(cacheName); + RedisCache redisCache = redisCacheManager.getOrCreate(cacheName); RedisProperties redisProperties = new RedisProperties(); diff --git a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubAutoConfiguration.java b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubAutoConfiguration.java index f823f6f310cc..0f3b13efffd6 100644 --- a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubAutoConfiguration.java +++ b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubAutoConfiguration.java @@ -4,11 +4,11 @@ package com.azure.spring.cloud.autoconfigure.eventhub; import com.azure.messaging.eventhubs.EventHubConsumerAsyncClient; -import com.microsoft.azure.management.eventhub.EventHubNamespace; -import com.microsoft.azure.management.storage.StorageAccount; -import com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration; +import com.azure.resourcemanager.storage.models.StorageAccount; import com.azure.spring.cloud.context.core.api.EnvironmentProvider; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; +import com.azure.spring.cloud.context.core.config.AzureProperties; +import com.azure.spring.cloud.context.core.impl.EventHubNamespaceManager; +import com.azure.spring.cloud.context.core.impl.StorageAccountManager; import com.azure.spring.cloud.context.core.storage.StorageConnectionStringProvider; import com.azure.spring.cloud.telemetry.TelemetryCollector; import com.azure.spring.integration.eventhub.api.EventHubClientFactory; @@ -16,6 +16,8 @@ import com.azure.spring.integration.eventhub.factory.DefaultEventHubClientFactory; import com.azure.spring.integration.eventhub.factory.EventHubConnectionStringProvider; import com.azure.spring.integration.eventhub.impl.EventHubTemplate; +import com.microsoft.azure.management.Azure; +import com.microsoft.azure.management.eventhub.EventHubNamespace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -34,7 +36,8 @@ * @author Warren Zhu */ @Configuration -@AutoConfigureAfter(AzureContextAutoConfiguration.class) +@AutoConfigureAfter(name = { "com.microsoft.azure.spring.cloud.autoconfigure.storage.AzureStorageAutoConfiguration", + "com.microsoft.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration" }) @ConditionalOnClass(EventHubConsumerAsyncClient.class) @ConditionalOnProperty(value = "spring.cloud.azure.eventhub.enabled", matchIfMissing = true) @EnableConfigurationProperties(AzureEventHubProperties.class) @@ -43,7 +46,10 @@ public class AzureEventHubAutoConfiguration { private static final String NAMESPACE = "Namespace"; @Autowired(required = false) - private ResourceManagerProvider resourceManagerProvider; + private EventHubNamespaceManager eventHubNamespaceManager; + + @Autowired(required = false) + private StorageAccountManager storageAccountManager; @PostConstruct public void collectTelemetry() { @@ -58,18 +64,17 @@ public EventHubOperation eventHubOperation(EventHubClientFactory clientFactory) /** * Create a {@link EventHubConnectionStringProvider} bean. + * * @param eventHubProperties The Event Hubs properties. * @return The {@link EventHubConnectionStringProvider} bean. - * * @throws IllegalArgumentException If connection string is empty. */ @Bean @ConditionalOnMissingBean public EventHubConnectionStringProvider eventHubConnectionStringProvider( AzureEventHubProperties eventHubProperties) { - if (resourceManagerProvider != null) { - EventHubNamespace namespace = resourceManagerProvider.getEventHubNamespaceManager() - .getOrCreate(eventHubProperties.getNamespace()); + if (eventHubNamespaceManager != null) { + EventHubNamespace namespace = eventHubNamespaceManager.getOrCreate(eventHubProperties.getNamespace()); return new EventHubConnectionStringProvider(namespace); } else { String connectionString = eventHubProperties.getConnectionString(); @@ -78,8 +83,8 @@ public EventHubConnectionStringProvider eventHubConnectionStringProvider( throw new IllegalArgumentException("Event hubs connection string cannot be empty"); } - TelemetryCollector.getInstance() - .addProperty(EVENT_HUB, NAMESPACE, EventHubUtils.getNamespace(connectionString)); + TelemetryCollector.getInstance().addProperty(EVENT_HUB, NAMESPACE, + EventHubUtils.getNamespace(connectionString)); return new EventHubConnectionStringProvider(connectionString); } } @@ -87,20 +92,29 @@ public EventHubConnectionStringProvider eventHubConnectionStringProvider( @Bean @ConditionalOnMissingBean public EventHubClientFactory clientFactory(EventHubConnectionStringProvider connectionStringProvider, - AzureEventHubProperties eventHubProperties, EnvironmentProvider environmentProvider) { + AzureEventHubProperties eventHubProperties, + EnvironmentProvider environmentProvider) { String checkpointConnectionString; - if (resourceManagerProvider != null) { - StorageAccount checkpointStorageAccount = resourceManagerProvider.getStorageAccountManager().getOrCreate( - eventHubProperties.getCheckpointStorageAccount()); - checkpointConnectionString = StorageConnectionStringProvider - .getConnectionString(checkpointStorageAccount, environmentProvider.getEnvironment()); + if (storageAccountManager != null) { + StorageAccount checkpointStorageAccount = storageAccountManager + .getOrCreate(eventHubProperties.getCheckpointStorageAccount()); + checkpointConnectionString = StorageConnectionStringProvider.getConnectionString(checkpointStorageAccount, + environmentProvider.getEnvironment()); } else { - checkpointConnectionString = StorageConnectionStringProvider - .getConnectionString(eventHubProperties.getCheckpointStorageAccount(), - eventHubProperties.getCheckpointAccessKey(), environmentProvider.getEnvironment()); + checkpointConnectionString = StorageConnectionStringProvider.getConnectionString( + eventHubProperties.getCheckpointStorageAccount(), eventHubProperties.getCheckpointAccessKey(), + environmentProvider.getEnvironment()); } return new DefaultEventHubClientFactory(connectionStringProvider, checkpointConnectionString, eventHubProperties.getCheckpointContainer()); } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty("spring.cloud.azure.resource-group") + public EventHubNamespaceManager eventHubNamespaceManager(Azure azure, AzureProperties azureProperties) { + return new EventHubNamespaceManager(azure, azureProperties); + } + } diff --git a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubKafkaAutoConfiguration.java b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubKafkaAutoConfiguration.java index 0b5b5646969f..1c433534f7fd 100644 --- a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubKafkaAutoConfiguration.java +++ b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubKafkaAutoConfiguration.java @@ -3,12 +3,12 @@ package com.azure.spring.cloud.autoconfigure.eventhub; +import com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration; +import com.azure.spring.cloud.context.core.impl.EventHubNamespaceManager; +import com.azure.spring.cloud.telemetry.TelemetryCollector; import com.microsoft.azure.management.eventhub.AuthorizationRule; import com.microsoft.azure.management.eventhub.EventHubAuthorizationKey; import com.microsoft.azure.management.eventhub.EventHubNamespace; -import com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; -import com.azure.spring.cloud.telemetry.TelemetryCollector; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -22,6 +22,7 @@ import javax.annotation.PostConstruct; import java.util.Arrays; + /** * An auto-configuration for Event Hub, which provides {@link KafkaProperties} * @@ -36,9 +37,8 @@ public class AzureEventHubKafkaAutoConfiguration { private static final String SECURITY_PROTOCOL = "security.protocol"; private static final String SASL_SSL = "SASL_SSL"; private static final String SASL_JAAS_CONFIG = "sasl.jaas.config"; - private static final String SASL_CONFIG_VALUE = - "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"$ConnectionString\" " - + "password=\"%s\";%n"; + private static final String SASL_CONFIG_VALUE = "org.apache.kafka.common.security.plain.PlainLoginModule required" + + " username=\"$ConnectionString\" " + "password=\"%s\";%n"; private static final String SASL_MECHANISM = "sasl.mechanism"; private static final String SASL_MECHANISM_PLAIN = "PLAIN"; private static final int PORT = 9093; @@ -52,15 +52,17 @@ public void collectTelemetry() { @SuppressWarnings("rawtypes") @Primary @Bean - public KafkaProperties kafkaProperties(ResourceManagerProvider resourceManagerProvider, + public KafkaProperties kafkaProperties(EventHubNamespaceManager eventHubNamespaceManager, AzureEventHubProperties eventHubProperties) { KafkaProperties kafkaProperties = new KafkaProperties(); - EventHubNamespace namespace = - resourceManagerProvider.getEventHubNamespaceManager().getOrCreate(eventHubProperties.getNamespace()); - String connectionString = - namespace.listAuthorizationRules().stream().findFirst().map(AuthorizationRule::getKeys) - .map(EventHubAuthorizationKey::primaryConnectionString).orElseThrow(() -> new RuntimeException( - String.format("Failed to fetch connection string of namespace '%s'", namespace), null)); + EventHubNamespace namespace = eventHubNamespaceManager.getOrCreate(eventHubProperties.getNamespace()); + String connectionString = namespace.listAuthorizationRules().stream() + .findFirst() + .map(AuthorizationRule::getKeys) + .map(EventHubAuthorizationKey::primaryConnectionString) + .orElseThrow(() -> new RuntimeException( + String.format("Failed to fetch connection string of namespace '%s'", + namespace), null)); String endpoint = namespace.serviceBusEndpoint(); String endpointHost = endpoint.substring("https://".length(), endpoint.lastIndexOf(':')); kafkaProperties.setBootstrapServers(Arrays.asList(endpointHost + ":" + PORT)); diff --git a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusAutoConfiguration.java b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusAutoConfiguration.java index a9cff2f3c39f..65198324f42b 100644 --- a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusAutoConfiguration.java +++ b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusAutoConfiguration.java @@ -5,7 +5,7 @@ import com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration; import com.azure.spring.cloud.context.core.api.ResourceManager; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; +import com.azure.spring.cloud.context.core.impl.ServiceBusNamespaceManager; import com.azure.spring.cloud.telemetry.TelemetryCollector; import com.microsoft.azure.management.servicebus.AuthorizationKeys; import com.microsoft.azure.management.servicebus.ServiceBusNamespace; @@ -57,15 +57,13 @@ public void collectTelemetry() { } @Bean - @ConditionalOnBean(ResourceManagerProvider.class) - public AzureServiceBusProperties serviceBusProperties(ResourceManagerProvider resourceManagerProvider, + @ConditionalOnBean(ServiceBusNamespaceManager.class) + public AzureServiceBusProperties serviceBusProperties(ServiceBusNamespaceManager serviceBusNamespaceManager, AzureServiceBusProperties serviceBusProperties) { if (!StringUtils.hasText(serviceBusProperties.getConnectionString())) { - ResourceManager serviceBusNamespaceManager = - resourceManagerProvider.getServiceBusNamespaceManager(); serviceBusNamespaceManager.getOrCreate(serviceBusProperties.getNamespace()); serviceBusProperties.setConnectionString( - buildConnectionString(resourceManagerProvider.getServiceBusNamespaceManager(), + buildConnectionString(serviceBusNamespaceManager, serviceBusProperties.getNamespace())); LOG.info("'spring.cloud.azure.servicebus.connection-string' auto configured"); } diff --git a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusQueueAutoConfiguration.java b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusQueueAutoConfiguration.java index 6a796817ec49..09eccc9956c7 100644 --- a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusQueueAutoConfiguration.java +++ b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusQueueAutoConfiguration.java @@ -3,14 +3,17 @@ package com.azure.spring.cloud.autoconfigure.servicebus; -import com.microsoft.azure.servicebus.QueueClient; import com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; +import com.azure.spring.cloud.context.core.config.AzureProperties; +import com.azure.spring.cloud.context.core.impl.ServiceBusNamespaceManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusQueueManager; import com.azure.spring.cloud.telemetry.TelemetryCollector; import com.azure.spring.integration.servicebus.factory.DefaultServiceBusQueueClientFactory; import com.azure.spring.integration.servicebus.factory.ServiceBusQueueClientFactory; import com.azure.spring.integration.servicebus.queue.ServiceBusQueueOperation; import com.azure.spring.integration.servicebus.queue.ServiceBusQueueTemplate; +import com.microsoft.azure.management.Azure; +import com.microsoft.azure.servicebus.QueueClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -28,14 +31,18 @@ */ @Configuration @AutoConfigureAfter(AzureContextAutoConfiguration.class) -@ConditionalOnClass(value = {QueueClient.class, ServiceBusQueueClientFactory.class}) +@ConditionalOnClass(value = { QueueClient.class, ServiceBusQueueClientFactory.class }) @ConditionalOnProperty(value = "spring.cloud.azure.servicebus.enabled", matchIfMissing = true) public class AzureServiceBusQueueAutoConfiguration { private static final String SERVICE_BUS_QUEUE = "ServiceBusQueue"; private static final String NAMESPACE = "Namespace"; @Autowired(required = false) - private ResourceManagerProvider resourceManagerProvider; + private ServiceBusNamespaceManager serviceBusNamespaceManager; + + @Autowired(required = false) + private ServiceBusQueueManager serviceBusQueueManager; + @PostConstruct public void collectTelemetry() { @@ -46,11 +53,12 @@ public void collectTelemetry() { @ConditionalOnMissingBean public ServiceBusQueueClientFactory queueClientFactory(AzureServiceBusProperties serviceBusProperties) { String connectionString = serviceBusProperties.getConnectionString(); - DefaultServiceBusQueueClientFactory clientFactory = - new DefaultServiceBusQueueClientFactory(serviceBusProperties.getConnectionString()); + DefaultServiceBusQueueClientFactory clientFactory = new DefaultServiceBusQueueClientFactory( + serviceBusProperties.getConnectionString()); - if (resourceManagerProvider != null) { - clientFactory.setResourceManagerProvider(resourceManagerProvider); + if (serviceBusNamespaceManager != null && serviceBusQueueManager != null) { + clientFactory.setServiceBusNamespaceManager(serviceBusNamespaceManager); + clientFactory.setServiceBusQueueManager(serviceBusQueueManager); clientFactory.setNamespace(serviceBusProperties.getNamespace()); } else { TelemetryCollector.getInstance().addProperty(SERVICE_BUS_QUEUE, NAMESPACE, @@ -65,4 +73,18 @@ public ServiceBusQueueClientFactory queueClientFactory(AzureServiceBusProperties public ServiceBusQueueOperation queueOperation(ServiceBusQueueClientFactory factory) { return new ServiceBusQueueTemplate(factory); } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty("spring.cloud.azure.resource-group") + public ServiceBusNamespaceManager serviceBusNamespaceManager(Azure azure, AzureProperties azureProperties) { + return new ServiceBusNamespaceManager(azure, azureProperties); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty("spring.cloud.azure.resource-group") + public ServiceBusQueueManager serviceBusQueueManager(AzureProperties azureProperties) { + return new ServiceBusQueueManager(azureProperties); + } } diff --git a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusTopicAutoConfiguration.java b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusTopicAutoConfiguration.java index a62cb4370c86..aad29735542a 100644 --- a/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusTopicAutoConfiguration.java +++ b/sdk/spring/azure-spring-cloud-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/servicebus/AzureServiceBusTopicAutoConfiguration.java @@ -3,14 +3,18 @@ package com.azure.spring.cloud.autoconfigure.servicebus; -import com.microsoft.azure.servicebus.TopicClient; import com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; +import com.azure.spring.cloud.context.core.config.AzureProperties; +import com.azure.spring.cloud.context.core.impl.ServiceBusNamespaceManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusTopicManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusTopicSubscriptionManager; import com.azure.spring.cloud.telemetry.TelemetryCollector; import com.azure.spring.integration.servicebus.factory.DefaultServiceBusTopicClientFactory; import com.azure.spring.integration.servicebus.factory.ServiceBusTopicClientFactory; import com.azure.spring.integration.servicebus.topic.ServiceBusTopicOperation; import com.azure.spring.integration.servicebus.topic.ServiceBusTopicTemplate; +import com.microsoft.azure.management.Azure; +import com.microsoft.azure.servicebus.TopicClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -35,7 +39,13 @@ public class AzureServiceBusTopicAutoConfiguration { private static final String NAMESPACE = "Namespace"; @Autowired(required = false) - private ResourceManagerProvider resourceManagerProvider; + private ServiceBusNamespaceManager serviceBusNamespaceManager; + + @Autowired(required = false) + private ServiceBusTopicManager serviceBusTopicManager; + + @Autowired(required = false) + private ServiceBusTopicSubscriptionManager serviceBusTopicSubscriptionManager; @PostConstruct public void collectTelemetry() { @@ -46,12 +56,14 @@ public void collectTelemetry() { @ConditionalOnMissingBean public ServiceBusTopicClientFactory topicClientFactory(AzureServiceBusProperties serviceBusProperties) { String connectionString = serviceBusProperties.getConnectionString(); - DefaultServiceBusTopicClientFactory clientFactory = - new DefaultServiceBusTopicClientFactory(serviceBusProperties.getConnectionString()); + DefaultServiceBusTopicClientFactory clientFactory = new DefaultServiceBusTopicClientFactory( + serviceBusProperties.getConnectionString()); - if (resourceManagerProvider != null) { + if (serviceBusTopicSubscriptionManager != null && serviceBusNamespaceManager != null) { clientFactory.setNamespace(serviceBusProperties.getNamespace()); - clientFactory.setResourceManagerProvider(resourceManagerProvider); + clientFactory.setServiceBusNamespaceManager(serviceBusNamespaceManager); + clientFactory.setServiceBusTopicManager(serviceBusTopicManager); + clientFactory.setServiceBusTopicSubscriptionManager(serviceBusTopicSubscriptionManager); } else { TelemetryCollector.getInstance().addProperty(SERVICE_BUS_TOPIC, NAMESPACE, ServiceBusUtils.getNamespace(connectionString)); @@ -60,9 +72,31 @@ public ServiceBusTopicClientFactory topicClientFactory(AzureServiceBusProperties return clientFactory; } + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty("spring.cloud.azure.resource-group") + public ServiceBusNamespaceManager serviceBusNamespaceManager(Azure azure, AzureProperties azureProperties) { + return new ServiceBusNamespaceManager(azure, azureProperties); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty("spring.cloud.azure.resource-group") + public ServiceBusTopicSubscriptionManager serviceBusTopicSubscriptionManager(AzureProperties azureProperties) { + return new ServiceBusTopicSubscriptionManager(azureProperties); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty("spring.cloud.azure.resource-group") + public ServiceBusTopicManager serviceBusTopicManager(AzureProperties azureProperties) { + return new ServiceBusTopicManager(azureProperties); + } + @Bean @ConditionalOnMissingBean public ServiceBusTopicOperation topicOperation(ServiceBusTopicClientFactory factory) { return new ServiceBusTopicTemplate(factory); } + } diff --git a/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/cache/AzureRedisAutoConfigurationTest.java b/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/cache/AzureRedisAutoConfigurationTest.java index b191482ae191..13e5aa9255be 100644 --- a/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/cache/AzureRedisAutoConfigurationTest.java +++ b/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/cache/AzureRedisAutoConfigurationTest.java @@ -3,10 +3,9 @@ package com.azure.spring.cloud.autoconfigure.cache; +import com.azure.spring.cloud.context.core.impl.RedisCacheManager; import com.microsoft.azure.management.redis.RedisAccessKeys; import com.microsoft.azure.management.redis.RedisCache; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; -import com.azure.spring.cloud.context.core.impl.RedisCacheManager; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; @@ -66,9 +65,9 @@ public void testAzureRedisPropertiesConfigured() { static class TestConfiguration { @Bean - ResourceManagerProvider resourceManagerProvider() { + RedisCacheManager redisCacheManager() { + - ResourceManagerProvider resourceManagerProvider = mock(ResourceManagerProvider.class); RedisCacheManager redisCacheManager = mock(RedisCacheManager.class); RedisCache redisCache = mock(RedisCache.class); RedisAccessKeys accessKeys = mock(RedisAccessKeys.class); @@ -78,9 +77,8 @@ ResourceManagerProvider resourceManagerProvider() { when(redisCache.sslPort()).thenReturn(PORT); when(redisCache.shardCount()).thenReturn(0); when(redisCache.getKeys()).thenReturn(accessKeys); - when(resourceManagerProvider.getRedisCacheManager()).thenReturn(redisCacheManager); when(redisCacheManager.getOrCreate(isA(String.class))).thenReturn(redisCache); - return resourceManagerProvider; + return redisCacheManager; } } diff --git a/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/context/AzureContextAutoConfigurationTest.java b/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/context/AzureContextAutoConfigurationTest.java index 066e09b29772..13848db0ef87 100644 --- a/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/context/AzureContextAutoConfigurationTest.java +++ b/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/context/AzureContextAutoConfigurationTest.java @@ -3,68 +3,106 @@ package com.azure.spring.cloud.autoconfigure.context; -import com.microsoft.azure.AzureEnvironment; -import com.microsoft.azure.credentials.AzureTokenCredentials; -import com.microsoft.azure.management.Azure; -import com.azure.spring.cloud.context.core.api.CredentialsProvider; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; -import com.azure.spring.cloud.context.core.config.AzureProperties; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; + import org.junit.Test; +import org.mockito.Mockito; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; +import com.azure.spring.cloud.context.core.api.CredentialsProvider; +import com.azure.spring.cloud.context.core.config.AzureProperties; +import com.microsoft.azure.AzureEnvironment; +import com.microsoft.azure.credentials.AzureTokenCredentials; +import com.microsoft.azure.management.Azure; public class AzureContextAutoConfigurationTest { - private ApplicationContextRunner contextRunner = - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(AzureContextAutoConfiguration.class)) + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(AzureContextAutoConfiguration.class)) .withUserConfiguration(TestConfiguration.class); @Test public void testAzurePropertiesConfigured() { this.contextRunner.withPropertyValues("spring.cloud.azure.credentialFilePath=credential") - .withPropertyValues("spring.cloud.azure.resourceGroup=group1") - .withPropertyValues("spring.cloud.azure.region=westUS").run(context -> { - assertThat(context).hasSingleBean(AzureProperties.class); - assertThat(context.getBean(AzureProperties.class).getCredentialFilePath()).isEqualTo("credential"); - assertThat(context.getBean(AzureProperties.class).getResourceGroup()).isEqualTo("group1"); - assertThat(context.getBean(AzureProperties.class).getRegion()).isEqualTo("westUS"); - assertThat(context.getBean(AzureProperties.class).getEnvironment()).isEqualTo(AzureEnvironment.AZURE); - }); + .withPropertyValues("spring.cloud.azure.resourceGroup=group1") + .withPropertyValues("spring.cloud.azure.region=westUS").run(context -> { + assertThat(context).hasSingleBean(AzureProperties.class); + assertThat(context.getBean(AzureProperties.class).getCredentialFilePath()).isEqualTo("credential"); + assertThat(context.getBean(AzureProperties.class).getResourceGroup()).isEqualTo("group1"); + assertThat(context.getBean(AzureProperties.class).getRegion()).isEqualTo("westUS"); + assertThat(context.getBean(AzureProperties.class).getEnvironment()) + .isEqualTo(AzureEnvironment.AZURE); + }); } @Test public void testRequiredAzureProperties() { this.contextRunner.withPropertyValues("spring.cloud.azure.credentialFilePath=credential") - .withPropertyValues("spring.cloud.azure.resourceGroup=group1").run(context -> { - assertThat(context).hasSingleBean(AzureProperties.class); - assertThat(context.getBean(AzureProperties.class).getCredentialFilePath()).isEqualTo("credential"); - assertThat(context.getBean(AzureProperties.class).getResourceGroup()).isEqualTo("group1"); - }); + .withPropertyValues("spring.cloud.azure.resourceGroup=group1").run(context -> { + assertThat(context).hasSingleBean(AzureProperties.class); + assertThat(context.getBean(AzureProperties.class).getCredentialFilePath()).isEqualTo("credential"); + assertThat(context.getBean(AzureProperties.class).getResourceGroup()).isEqualTo("group1"); + }); } @Test public void testAzureDisabled() { this.contextRunner.withPropertyValues("spring.cloud.azure.enabled=false") - .run(context -> assertThat(context).doesNotHaveBean(AzureProperties.class)); + .run(context -> assertThat(context).doesNotHaveBean(AzureProperties.class)); } @Test public void testWithoutAzureClass() { this.contextRunner.withClassLoader(new FilteredClassLoader(Azure.class)) - .run(context -> assertThat(context).doesNotHaveBean(AzureProperties.class)); + .run(context -> assertThat(context).doesNotHaveBean(AzureProperties.class)); } @Test(expected = IllegalStateException.class) public void testLocationRequiredWhenAutoCreateResources() { this.contextRunner.withPropertyValues("spring.cloud.azure.credentialFilePath=credential") - .withPropertyValues("spring.cloud.azure.resourceGroup=group1") - .withPropertyValues("spring.cloud.azure.auto-create-resources=true") - .run(context -> context.getBean(AzureProperties.class)); + .withPropertyValues("spring.cloud.azure.resourceGroup=group1") + .withPropertyValues("spring.cloud.azure.auto-create-resources=true") + .run(context -> context.getBean(AzureProperties.class)); + } + + @Test + // Ensure a default subscription ID is correctly used when not specified in the + // properties + public void testDefaultSubscriptionId() throws IOException { + AzureProperties azureProperties = new AzureProperties(); + String expectedSubscriptionId = "non-default-subscription-id"; + + // Mock credentials + AzureTokenCredentials mockCredentials = mock(AzureTokenCredentials.class); + when(mockCredentials.domain()).thenReturn("testdomain"); + when(mockCredentials.environment()).thenReturn(AzureEnvironment.AZURE); + when(mockCredentials.defaultSubscriptionId()).thenReturn(expectedSubscriptionId); + + + // Call real auto-config logic with stubbed-out connectivity + AzureContextAutoConfiguration mockAutoConfig = mock(AzureContextAutoConfiguration.class); + when(mockAutoConfig.azure(any(), any())).thenCallRealMethod(); + when(mockAutoConfig.authenticateToAzure(any(), anyString(), any())).then(invocation -> { + assertEquals(expectedSubscriptionId, invocation.getArgument(1)); + return mock(Azure.class); + }); + mockAutoConfig.azure(mockCredentials, azureProperties); + verify(mockAutoConfig, times(1)).authenticateToAzure(any(), eq(expectedSubscriptionId), any()); + } @Configuration @@ -80,11 +118,6 @@ CredentialsProvider credentialsProvider() { return mock(CredentialsProvider.class); } - @Bean - ResourceManagerProvider resourceManagerProvider() { - return mock(ResourceManagerProvider.class); - } - @Bean AzureTokenCredentials credentials() { return mock(AzureTokenCredentials.class); diff --git a/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubKafkaAutoConfigurationTest.java b/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubKafkaAutoConfigurationTest.java index 28ac6c1a8524..d941d796ca4b 100644 --- a/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubKafkaAutoConfigurationTest.java +++ b/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/eventhub/AzureEventHubKafkaAutoConfigurationTest.java @@ -3,12 +3,12 @@ package com.azure.spring.cloud.autoconfigure.eventhub; +import com.azure.spring.cloud.context.core.impl.EventHubNamespaceManager; import com.microsoft.azure.Page; import com.microsoft.azure.PagedList; import com.microsoft.azure.management.eventhub.EventHubAuthorizationKey; import com.microsoft.azure.management.eventhub.EventHubNamespace; import com.microsoft.azure.management.eventhub.EventHubNamespaceAuthorizationRule; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; import com.microsoft.rest.RestException; import org.junit.Ignore; import org.junit.Test; @@ -72,9 +72,8 @@ public void testAzureEventHubPropertiesConfigured() { static class TestConfiguration { @Bean - ResourceManagerProvider resourceManagerProvider() { + EventHubNamespaceManager eventHubNamespaceManager() { - ResourceManagerProvider resourceManagerProvider = mock(ResourceManagerProvider.class); EventHubNamespace namespace = mock(EventHubNamespace.class); EventHubAuthorizationKey key = mock(EventHubAuthorizationKey.class); when(key.primaryConnectionString()).thenReturn("connectionString1"); @@ -90,7 +89,10 @@ public Page nextPage(String nextPageLink) rules.add(rule); when(namespace.listAuthorizationRules()).thenReturn(rules); when(namespace.serviceBusEndpoint()).thenReturn("localhost"); - return resourceManagerProvider; + EventHubNamespaceManager eventHubNamespaceManager = mock(EventHubNamespaceManager.class); + //This previously returned a ResourceManagerProvider that was in no way connected to any of the objets created above. + //Maintaining similar behavior in refactoring. + return eventHubNamespaceManager; } } diff --git a/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/servicebus/ServiceBusTestConfiguration.java b/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/servicebus/ServiceBusTestConfiguration.java index 27a269115d62..cecdf73d855f 100644 --- a/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/servicebus/ServiceBusTestConfiguration.java +++ b/sdk/spring/azure-spring-cloud-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/servicebus/ServiceBusTestConfiguration.java @@ -3,9 +3,8 @@ package com.azure.spring.cloud.autoconfigure.servicebus; -import com.microsoft.azure.management.servicebus.ServiceBusNamespace; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; import com.azure.spring.cloud.context.core.impl.ServiceBusNamespaceManager; +import com.microsoft.azure.management.servicebus.ServiceBusNamespace; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -16,11 +15,9 @@ @Configuration public class ServiceBusTestConfiguration { @Bean - ResourceManagerProvider resourceManagerProvider() { - ResourceManagerProvider resourceManagerProvider = mock(ResourceManagerProvider.class); + ServiceBusNamespaceManager namespaceManager() { ServiceBusNamespaceManager namespaceManager = mock(ServiceBusNamespaceManager.class); when(namespaceManager.getOrCreate(anyString())).thenReturn(mock(ServiceBusNamespace.class)); - when(resourceManagerProvider.getServiceBusNamespaceManager()).thenReturn(namespaceManager); - return resourceManagerProvider; + return namespaceManager; } } diff --git a/sdk/spring/azure-spring-cloud-context/pom.xml b/sdk/spring/azure-spring-cloud-context/pom.xml index 9dbb79f206b4..6d17f42e030b 100644 --- a/sdk/spring/azure-spring-cloud-context/pom.xml +++ b/sdk/spring/azure-spring-cloud-context/pom.xml @@ -63,14 +63,24 @@ true - + - com.google.code.findbugs - jsr305 - 3.0.2 - provided + com.azure.resourcemanager + azure-resourcemanager + 2.0.0 + + + + com.azure.resourcemanager + azure-resourcemanager-storage + 2.0.0 + + + + + com.azure + azure-identity-spring-library + 1.0.0-beta.1 @@ -93,6 +103,15 @@ test + + + com.google.code.findbugs + jsr305 + 3.0.2 + provided + @@ -107,7 +126,8 @@ com.microsoft.azure:azure:[1.34.0] commons-io:commons-io:[2.5] - org.springframework.boot:spring-boot-configuration-processor:[2.3.4.RELEASE] + org.springframework.boot:spring-boot-configuration-processor:[2.3.4.RELEASE] + org.springframework.boot:spring-boot-starter-aop:[2.3.4.RELEASE] org.springframework:spring-context-support:[5.2.9.RELEASE] org.springframework:spring-context:[5.2.9.RELEASE] diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureContextAutoConfiguration.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureContextAutoConfiguration.java index 24685625b58e..428643707d38 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureContextAutoConfiguration.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureContextAutoConfiguration.java @@ -3,11 +3,21 @@ package com.azure.spring.cloud.autoconfigure.context; +import java.io.IOException; +import java.util.Optional; + +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.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + import com.azure.spring.cloud.context.core.api.CredentialsProvider; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; import com.azure.spring.cloud.context.core.config.AzureProperties; -import com.azure.spring.cloud.context.core.impl.AzureResourceManagerProvider; import com.azure.spring.cloud.context.core.impl.DefaultCredentialsProvider; +import com.google.common.annotations.VisibleForTesting; import com.microsoft.azure.AzureEnvironment; import com.microsoft.azure.AzureResponseBuilder; import com.microsoft.azure.credentials.AzureTokenCredentials; @@ -16,18 +26,10 @@ import com.microsoft.azure.management.resources.fluentcore.utils.ResourceManagerThrottlingInterceptor; import com.microsoft.azure.serializer.AzureJacksonAdapter; import com.microsoft.rest.RestClient; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.io.IOException; -import java.util.Optional; /** - * Auto-config to provide default {@link CredentialsProvider} for all Azure services + * Auto-config to provide default {@link CredentialsProvider} for all Azure + * services * * @author Warren Zhu */ @@ -35,17 +37,14 @@ @EnableConfigurationProperties(AzureProperties.class) @ConditionalOnClass(Azure.class) @ConditionalOnProperty(prefix = "spring.cloud.azure", value = { "resource-group" }) +@ConditionalOnExpression("#{ ${spring.cloud.azure.msi-enabled:false} or ('${spring.cloud.azure" + + ".credential-file-path:}' > '') }") public class AzureContextAutoConfiguration { - private static final String PROJECT_VERSION = - AzureContextAutoConfiguration.class.getPackage().getImplementationVersion(); + private static final String PROJECT_VERSION = AzureContextAutoConfiguration.class.getPackage() + .getImplementationVersion(); private static final String SPRING_CLOUD_USER_AGENT = "spring-cloud-azure/" + PROJECT_VERSION; - @Bean - @ConditionalOnMissingBean - public ResourceManagerProvider resourceManagerProvider(Azure azure, AzureProperties azureProperties) { - return new AzureResourceManagerProvider(azure, azureProperties); - } /** * Create an {@link Azure} bean. @@ -58,17 +57,23 @@ public ResourceManagerProvider resourceManagerProvider(Azure azure, AzurePropert @ConditionalOnMissingBean public Azure azure(AzureTokenCredentials credentials, AzureProperties azureProperties) throws IOException { RestClient restClient = new RestClient.Builder() - .withBaseUrl(credentials.environment(), AzureEnvironment.Endpoint.RESOURCE_MANAGER) - .withCredentials(credentials).withSerializerAdapter(new AzureJacksonAdapter()) - .withResponseBuilderFactory(new AzureResponseBuilder.Factory()) - .withInterceptor(new ProviderRegistrationInterceptor(credentials)) - .withInterceptor(new ResourceManagerThrottlingInterceptor()).withUserAgent(SPRING_CLOUD_USER_AGENT) - .build(); + .withBaseUrl(credentials.environment(), AzureEnvironment.Endpoint.RESOURCE_MANAGER) + .withCredentials(credentials).withSerializerAdapter(new AzureJacksonAdapter()) + .withResponseBuilderFactory(new AzureResponseBuilder.Factory()) + .withInterceptor(new ProviderRegistrationInterceptor(credentials)) + .withInterceptor(new ResourceManagerThrottlingInterceptor()).withUserAgent(SPRING_CLOUD_USER_AGENT) + .build(); + + String subscriptionId = Optional.ofNullable(azureProperties.getSubscriptionId()) + .orElseGet(credentials::defaultSubscriptionId); + + return authenticateToAzure(restClient, subscriptionId, credentials); + } - String subscriptionId = Optional.of(azureProperties.getSubscriptionId()) - .orElseGet(credentials::defaultSubscriptionId); - return Azure.authenticate(restClient, credentials.domain()) - .withSubscription(subscriptionId); + @VisibleForTesting + protected Azure authenticateToAzure(RestClient restClient, String subscriptionId, + AzureTokenCredentials credentials) { + return Azure.authenticate(restClient, credentials.domain()).withSubscription(subscriptionId); } @Bean diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureEnvironmentAutoConfiguration.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureEnvironmentAutoConfiguration.java index 8c468ce79c6e..1d35bd0bc100 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureEnvironmentAutoConfiguration.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureEnvironmentAutoConfiguration.java @@ -32,4 +32,7 @@ public EnvironmentProvider environmentProvider() { return defaultEnvironmentProvider; } + + + } diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureResourceManager20AutoConfiguration.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureResourceManager20AutoConfiguration.java new file mode 100644 index 000000000000..7bfd8768cd65 --- /dev/null +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/AzureResourceManager20AutoConfiguration.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.autoconfigure.context; + +import com.azure.core.credential.TokenCredential; +import com.azure.core.management.profile.AzureProfile; +import com.azure.resourcemanager.AzureResourceManager; +import com.azure.spring.cloud.context.core.config.AzureProperties; +import com.microsoft.azure.AzureEnvironment; +import com.microsoft.azure.credentials.AzureTokenCredentials; +import com.microsoft.azure.identity.spring.SpringEnvironmentTokenBuilder; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +/** + * Auto-configure the new {@link AzureResourceManager}. + */ +@Configuration +@EnableConfigurationProperties(AzureProperties.class) +@ConditionalOnClass(AzureResourceManager.class) +@ConditionalOnProperty(prefix = "spring.cloud.azure", value = { "resource-group" }) +public class AzureResourceManager20AutoConfiguration { + + private static final Logger LOGGER = LoggerFactory.getLogger(AzureResourceManager20AutoConfiguration.class); + + @Autowired(required = false) + private AzureTokenCredentials credentials; + + @Bean + @ConditionalOnMissingBean + public AzureResourceManager.Authenticated azure20(TokenCredential tokenCredential, + AzureProperties azureProperties) { + AzureEnvironment legacyEnvironment = azureProperties.getEnvironment(); + com.azure.core.management.AzureEnvironment azureEnvironment = com.azure.core.management.AzureEnvironment + .knownEnvironments().stream() + .filter(env -> env.getManagementEndpoint().equals(legacyEnvironment.managementEndpoint())) + .findFirst() + .get(); + return AzureResourceManager.authenticate(tokenCredential, new AzureProfile(azureEnvironment)); + } + + @Bean + @ConditionalOnMissingBean + public TokenCredential tokenCredential(Environment environment, AzureProperties azureProperties) { + SpringEnvironmentTokenBuilder builder = new SpringEnvironmentTokenBuilder().fromEnvironment(environment); + if (!StringUtils.isBlank(azureProperties.getCredentialFilePath())) { + if (credentials == null) { + LOGGER.error("Legacy azure credentials not initialized though credential-file-path was provided"); + } + builder.overrideNamedCredential("", new LegacyTokenCredentialAdapter(credentials)); + } + return builder.build(); + } +} diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/LegacyTokenCredentialAdapter.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/LegacyTokenCredentialAdapter.java new file mode 100644 index 000000000000..d21790b037e5 --- /dev/null +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/autoconfigure/context/LegacyTokenCredentialAdapter.java @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.autoconfigure.context; + +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenCredential; +import com.azure.core.credential.TokenRequestContext; +import com.azure.identity.implementation.ScopeUtil; +import com.microsoft.azure.credentials.AzureTokenCredentials; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.time.OffsetDateTime; + +/** + * Adapt the legacy token credential {@link AzureTokenCredentials} to {@link TokenCredential}. + */ +public class LegacyTokenCredentialAdapter implements TokenCredential { + + private final AzureTokenCredentials azureTokenCredentials; + + public LegacyTokenCredentialAdapter(AzureTokenCredentials azureTokenCredentials) { + this.azureTokenCredentials = azureTokenCredentials; + } + + @Override + public Mono getToken(TokenRequestContext request) { + String resource = ScopeUtil.scopesToResource(request.getScopes()); + try { + return Mono.just(new AccessToken(azureTokenCredentials.getToken(resource), OffsetDateTime.MAX)); + } catch (IOException e) { + throw new CredentialUnavailableException(e); + } + } + + /** + * Thrown on failure to obtain a credential for the requested resource. + * @author yebronsh + * + */ + public static final class CredentialUnavailableException extends RuntimeException { + /** + * + */ + private static final long serialVersionUID = -5389931452186532039L; + + public CredentialUnavailableException(Throwable cause) { + super(cause); + } + } +} diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/api/EnvironmentProvider.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/api/EnvironmentProvider.java index 4145c16cc58d..56f255875e08 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/api/EnvironmentProvider.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/api/EnvironmentProvider.java @@ -13,4 +13,16 @@ public interface EnvironmentProvider { AzureEnvironment getEnvironment(); + + /** + * @return The Azure environment as defined by the + * com.azure.core.management SDK. + */ + default com.azure.core.management.AzureEnvironment getCoreEnvironment() { + return com.azure.core.management.AzureEnvironment.knownEnvironments().stream() + .filter(coreEnvironment -> getEnvironment().managementEndpoint() + .equals(coreEnvironment.getManagementEndpoint())) + .findAny().get(); + } + } diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/api/ResourceManagerProvider.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/api/ResourceManagerProvider.java deleted file mode 100644 index 9104b9c03ab4..000000000000 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/api/ResourceManagerProvider.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.spring.cloud.context.core.api; - -import com.microsoft.azure.management.eventhub.EventHub; -import com.microsoft.azure.management.eventhub.EventHubConsumerGroup; -import com.microsoft.azure.management.eventhub.EventHubNamespace; -import com.microsoft.azure.management.redis.RedisCache; -import com.microsoft.azure.management.resources.ResourceGroup; -import com.microsoft.azure.management.servicebus.Queue; -import com.microsoft.azure.management.servicebus.ServiceBusNamespace; -import com.microsoft.azure.management.servicebus.ServiceBusSubscription; -import com.microsoft.azure.management.servicebus.Topic; -import com.microsoft.azure.management.storage.StorageAccount; -import com.azure.spring.cloud.context.core.util.Tuple; -import com.microsoft.azure.storage.CloudStorageAccount; -import com.microsoft.azure.storage.queue.CloudQueue; - -/** - * Interface to provide {@link ResourceManager} - * - * @author Warren Zhu - */ -public interface ResourceManagerProvider { - - ResourceManager> getEventHubManager(); - - ResourceManager> getEventHubConsumerGroupManager(); - - ResourceManager getResourceGroupManager(); - - ResourceManager getEventHubNamespaceManager(); - - ResourceManager getRedisCacheManager(); - - ResourceManager getServiceBusNamespaceManager(); - - ResourceManager> getServiceBusQueueManager(); - - ResourceManager> getServiceBusTopicManager(); - - ResourceManager getStorageAccountManager(); - - ResourceManager> getStorageQueueManager(); - - ResourceManager> getServiceBusTopicSubscriptionManager(); -} diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/AzureManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/AzureManager.java index 03f899a17c3a..9930d3768545 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/AzureManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/AzureManager.java @@ -4,9 +4,8 @@ package com.azure.spring.cloud.context.core.impl; import com.azure.spring.cloud.context.core.api.ResourceManager; -import com.microsoft.azure.CloudException; -import com.microsoft.azure.management.Azure; import com.azure.spring.cloud.context.core.config.AzureProperties; +import com.microsoft.azure.CloudException; import org.apache.commons.lang3.time.StopWatch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,10 +21,8 @@ public abstract class AzureManager implements ResourceManager { private static final Logger LOGGER = LoggerFactory.getLogger(AzureManager.class); protected final AzureProperties azureProperties; - protected final Azure azure; - public AzureManager(@NonNull Azure azure, @NonNull AzureProperties azureProperties) { - this.azure = azure; + public AzureManager(@NonNull AzureProperties azureProperties) { this.azureProperties = azureProperties; } diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/AzureResourceManagerProvider.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/AzureResourceManagerProvider.java deleted file mode 100644 index 6b9a18300714..000000000000 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/AzureResourceManagerProvider.java +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.spring.cloud.context.core.impl; - -import com.azure.spring.cloud.context.core.api.ResourceManager; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; -import com.microsoft.azure.management.Azure; -import com.microsoft.azure.management.eventhub.EventHub; -import com.microsoft.azure.management.eventhub.EventHubConsumerGroup; -import com.microsoft.azure.management.eventhub.EventHubNamespace; -import com.microsoft.azure.management.redis.RedisCache; -import com.microsoft.azure.management.resources.ResourceGroup; -import com.microsoft.azure.management.servicebus.Queue; -import com.microsoft.azure.management.servicebus.ServiceBusNamespace; -import com.microsoft.azure.management.servicebus.ServiceBusSubscription; -import com.microsoft.azure.management.servicebus.Topic; -import com.microsoft.azure.management.storage.StorageAccount; -import com.azure.spring.cloud.context.core.config.AzureProperties; -import com.azure.spring.cloud.context.core.util.Tuple; -import com.azure.spring.cloud.context.core.util.TypeMap; -import com.microsoft.azure.storage.CloudStorageAccount; -import com.microsoft.azure.storage.queue.CloudQueue; -import org.springframework.lang.NonNull; - -/** - * Azure resource manager. - */ -public class AzureResourceManagerProvider implements ResourceManagerProvider { - - private final TypeMap resourceManagerByType = new TypeMap(); - - public AzureResourceManagerProvider(@NonNull Azure azure, @NonNull AzureProperties azureProperties) { - this.resourceManagerByType - .put(EventHubNamesapceManager.class, new EventHubNamesapceManager(azure, azureProperties)); - this.resourceManagerByType.put(EventHubManager.class, new EventHubManager(azure, azureProperties)); - this.resourceManagerByType - .put(EventHubConsumerGroupManager.class, new EventHubConsumerGroupManager(azure, azureProperties)); - this.resourceManagerByType.put(RedisCacheManager.class, new RedisCacheManager(azure, azureProperties)); - this.resourceManagerByType.put(ResourceGroupManager.class, new ResourceGroupManager(azure, azureProperties)); - this.resourceManagerByType - .put(ServiceBusNamespaceManager.class, new ServiceBusNamespaceManager(azure, azureProperties)); - this.resourceManagerByType - .put(ServiceBusQueueManager.class, new ServiceBusQueueManager(azure, azureProperties)); - this.resourceManagerByType - .put(ServiceBusTopicManager.class, new ServiceBusTopicManager(azure, azureProperties)); - this.resourceManagerByType.put(ServiceBusTopicSubscriptionManager.class, - new ServiceBusTopicSubscriptionManager(azure, azureProperties)); - this.resourceManagerByType.put(StorageAccountManager.class, new StorageAccountManager(azure, azureProperties)); - this.resourceManagerByType.put(StorageQueueManager.class, new StorageQueueManager(azure, azureProperties)); - this.getResourceGroupManager().getOrCreate(azureProperties.getResourceGroup()); - } - - @Override - public ResourceManager> getEventHubManager() { - return this.resourceManagerByType.get(EventHubManager.class); - } - - @Override - public ResourceManager> getEventHubConsumerGroupManager() { - return this.resourceManagerByType.get(EventHubConsumerGroupManager.class); - } - - @Override - public ResourceManager getResourceGroupManager() { - return this.resourceManagerByType.get(ResourceGroupManager.class); - } - - @Override - public ResourceManager getEventHubNamespaceManager() { - return this.resourceManagerByType.get(EventHubNamesapceManager.class); - } - - @Override - public ResourceManager getRedisCacheManager() { - return this.resourceManagerByType.get(RedisCacheManager.class); - } - - @Override - public ResourceManager getServiceBusNamespaceManager() { - return this.resourceManagerByType.get(ServiceBusNamespaceManager.class); - } - - @Override - public ResourceManager> getServiceBusQueueManager() { - return this.resourceManagerByType.get(ServiceBusQueueManager.class); - } - - @Override - public ResourceManager> getServiceBusTopicManager() { - return this.resourceManagerByType.get(ServiceBusTopicManager.class); - } - - @Override - public ResourceManager getStorageAccountManager() { - return this.resourceManagerByType.get(StorageAccountManager.class); - } - - @Override - public ResourceManager> getStorageQueueManager() { - return this.resourceManagerByType.get(StorageQueueManager.class); - } - - @Override - public ResourceManager> getServiceBusTopicSubscriptionManager() { - return this.resourceManagerByType.get(ServiceBusTopicSubscriptionManager.class); - } -} diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubConsumerGroupManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubConsumerGroupManager.java index 2f53da215028..a8700fe26cf5 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubConsumerGroupManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubConsumerGroupManager.java @@ -14,8 +14,11 @@ */ public class EventHubConsumerGroupManager extends AzureManager> { + private final Azure azure; + public EventHubConsumerGroupManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + super(azureProperties); + this.azure = azure; } @Override diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubManager.java index d2969831c119..ad3013137711 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubManager.java @@ -13,9 +13,12 @@ * Resource manager for Event Hubs. */ public class EventHubManager extends AzureManager> { + + private final Azure azure; public EventHubManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + super(azureProperties); + this.azure = azure; } @Override diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubNamesapceManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubNamespaceManager.java similarity index 77% rename from sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubNamesapceManager.java rename to sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubNamespaceManager.java index b8275cdfd29b..2ebdcee7ab3e 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubNamesapceManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/EventHubNamespaceManager.java @@ -3,17 +3,20 @@ package com.azure.spring.cloud.context.core.impl; +import com.azure.spring.cloud.context.core.config.AzureProperties; import com.microsoft.azure.management.Azure; import com.microsoft.azure.management.eventhub.EventHubNamespace; -import com.azure.spring.cloud.context.core.config.AzureProperties; /** * Resource manager for Event Hubs namespace. */ -public class EventHubNamesapceManager extends AzureManager { +public class EventHubNamespaceManager extends AzureManager { + + private final Azure azure; - public EventHubNamesapceManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + public EventHubNamespaceManager(Azure azure, AzureProperties azureProperties) { + super(azureProperties); + this.azure = azure; } @Override @@ -31,7 +34,8 @@ public EventHubNamespace internalGet(String namespace) { try { return azure.eventHubNamespaces().getByResourceGroup(azureProperties.getResourceGroup(), namespace); } catch (NullPointerException e) { - // azure management api has no way to determine whether an eventhub namespace exists + // azure management api has no way to determine whether an eventhub namespace + // exists // Workaround for this is by catching NPE return null; } @@ -40,6 +44,6 @@ public EventHubNamespace internalGet(String namespace) { @Override public EventHubNamespace internalCreate(String namespace) { return azure.eventHubNamespaces().define(namespace).withRegion(azureProperties.getRegion()) - .withExistingResourceGroup(azureProperties.getResourceGroup()).create(); + .withExistingResourceGroup(azureProperties.getResourceGroup()).create(); } } diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/RedisCacheManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/RedisCacheManager.java index e80d89ce4105..a8d47affa7d4 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/RedisCacheManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/RedisCacheManager.java @@ -12,8 +12,11 @@ */ public class RedisCacheManager extends AzureManager { + private final Azure azure; + public RedisCacheManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + super(azureProperties); + this.azure = azure; } @Override @@ -34,6 +37,6 @@ public RedisCache internalGet(String name) { @Override public RedisCache internalCreate(String name) { return azure.redisCaches().define(name).withRegion(azureProperties.getRegion()) - .withExistingResourceGroup(azureProperties.getResourceGroup()).withBasicSku().create(); + .withExistingResourceGroup(azureProperties.getResourceGroup()).withBasicSku().create(); } } diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ResourceGroupManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ResourceGroupManager.java index 0dcc80549ac4..43a8de067446 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ResourceGroupManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ResourceGroupManager.java @@ -12,8 +12,11 @@ */ public class ResourceGroupManager extends AzureManager { + private final Azure azure; + public ResourceGroupManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + super(azureProperties); + this.azure = azure; } @Override diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusNamespaceManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusNamespaceManager.java index a79260e20d47..b0c41dee78ff 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusNamespaceManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusNamespaceManager.java @@ -3,17 +3,20 @@ package com.azure.spring.cloud.context.core.impl; +import com.azure.spring.cloud.context.core.config.AzureProperties; import com.microsoft.azure.management.Azure; import com.microsoft.azure.management.servicebus.ServiceBusNamespace; -import com.azure.spring.cloud.context.core.config.AzureProperties; /** * Resource manager for Service Bus namespace. */ public class ServiceBusNamespaceManager extends AzureManager { + private final Azure azure; + public ServiceBusNamespaceManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + super(azureProperties); + this.azure = azure; } @Override @@ -31,7 +34,8 @@ public ServiceBusNamespace internalGet(String namespace) { try { return azure.serviceBusNamespaces().getByResourceGroup(azureProperties.getResourceGroup(), namespace); } catch (NullPointerException e) { - // azure management api has no way to determine whether an eventhub namespace exists + // azure management api has no way to determine whether an eventhub namespace + // exists // Workaround for this is by catching NPE return null; } @@ -40,6 +44,6 @@ public ServiceBusNamespace internalGet(String namespace) { @Override public ServiceBusNamespace internalCreate(String namespace) { return azure.serviceBusNamespaces().define(namespace).withRegion(azureProperties.getRegion()) - .withExistingResourceGroup(azureProperties.getResourceGroup()).create(); + .withExistingResourceGroup(azureProperties.getResourceGroup()).create(); } } diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusQueueManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusQueueManager.java index 0ddfb84fee61..4b8e801de3fa 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusQueueManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusQueueManager.java @@ -5,7 +5,6 @@ import com.azure.spring.cloud.context.core.config.AzureProperties; import com.azure.spring.cloud.context.core.util.Tuple; -import com.microsoft.azure.management.Azure; import com.microsoft.azure.management.servicebus.Queue; import com.microsoft.azure.management.servicebus.ServiceBusNamespace; @@ -14,8 +13,9 @@ */ public class ServiceBusQueueManager extends AzureManager> { - public ServiceBusQueueManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + + public ServiceBusQueueManager(AzureProperties azureProperties) { + super(azureProperties); } @Override diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusTopicManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusTopicManager.java index c19132498de2..8bd582cc8219 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusTopicManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusTopicManager.java @@ -3,7 +3,6 @@ package com.azure.spring.cloud.context.core.impl; -import com.microsoft.azure.management.Azure; import com.microsoft.azure.management.servicebus.ServiceBusNamespace; import com.microsoft.azure.management.servicebus.Topic; import com.azure.spring.cloud.context.core.config.AzureProperties; @@ -14,8 +13,8 @@ */ public class ServiceBusTopicManager extends AzureManager> { - public ServiceBusTopicManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + public ServiceBusTopicManager(AzureProperties azureProperties) { + super(azureProperties); } @Override diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusTopicSubscriptionManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusTopicSubscriptionManager.java index 9769ef84e40c..2d94722dc5a7 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusTopicSubscriptionManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/ServiceBusTopicSubscriptionManager.java @@ -3,7 +3,6 @@ package com.azure.spring.cloud.context.core.impl; -import com.microsoft.azure.management.Azure; import com.microsoft.azure.management.servicebus.ServiceBusSubscription; import com.microsoft.azure.management.servicebus.Topic; import com.azure.spring.cloud.context.core.config.AzureProperties; @@ -14,8 +13,9 @@ */ public class ServiceBusTopicSubscriptionManager extends AzureManager> { - public ServiceBusTopicSubscriptionManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + + public ServiceBusTopicSubscriptionManager(AzureProperties azureProperties) { + super(azureProperties); } @Override @@ -33,7 +33,8 @@ public ServiceBusSubscription internalGet(Tuple topicAndSubscript try { return topicAndSubscriptionName.getFirst().subscriptions().getByName(topicAndSubscriptionName.getSecond()); } catch (NullPointerException ignore) { - // azure management api has no way to determine whether an service bus topic subscription exists + // azure management api has no way to determine whether an service bus topic + // subscription exists // Workaround for this is by catching NPE return null; } @@ -42,6 +43,6 @@ public ServiceBusSubscription internalGet(Tuple topicAndSubscript @Override public ServiceBusSubscription internalCreate(Tuple topicAndSubscriptionName) { return topicAndSubscriptionName.getFirst().subscriptions().define(topicAndSubscriptionName.getSecond()) - .create(); + .create(); } } diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/StorageAccountManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/StorageAccountManager.java index 2869caac6be7..2f9de3ffecd5 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/StorageAccountManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/StorageAccountManager.java @@ -3,17 +3,22 @@ package com.azure.spring.cloud.context.core.impl; -import com.microsoft.azure.management.Azure; -import com.microsoft.azure.management.storage.StorageAccount; +import com.azure.resourcemanager.AzureResourceManager; +import com.azure.resourcemanager.storage.models.StorageAccount; import com.azure.spring.cloud.context.core.config.AzureProperties; +import javax.annotation.Nonnull; + /** * Resource manager for Storage account. */ public class StorageAccountManager extends AzureManager { - public StorageAccountManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + private final AzureResourceManager azureResourceManager; + + public StorageAccountManager(@Nonnull AzureResourceManager azureResourceManager, AzureProperties azureProperties) { + super(azureProperties); + this.azureResourceManager = azureResourceManager; } @Override @@ -28,12 +33,12 @@ String getResourceType() { @Override public StorageAccount internalGet(String key) { - return azure.storageAccounts().getByResourceGroup(azureProperties.getResourceGroup(), key); + return azureResourceManager.storageAccounts().getByResourceGroup(azureProperties.getResourceGroup(), key); } @Override public StorageAccount internalCreate(String key) { - return azure.storageAccounts().define(key).withRegion(azureProperties.getRegion()) - .withExistingResourceGroup(azureProperties.getResourceGroup()).create(); + return azureResourceManager.storageAccounts().define(key).withRegion(azureProperties.getRegion()) + .withExistingResourceGroup(azureProperties.getResourceGroup()).create(); } } diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/StorageQueueManager.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/StorageQueueManager.java index 118e6e80b8b9..4fa89441a904 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/StorageQueueManager.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/impl/StorageQueueManager.java @@ -3,13 +3,13 @@ package com.azure.spring.cloud.context.core.impl; -import com.microsoft.azure.management.Azure; import com.azure.spring.cloud.context.core.config.AzureProperties; import com.azure.spring.cloud.context.core.util.Tuple; import com.microsoft.azure.storage.CloudStorageAccount; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.queue.CloudQueue; import com.microsoft.azure.storage.queue.CloudQueueClient; + import java.net.URISyntaxException; /** @@ -17,8 +17,8 @@ */ public class StorageQueueManager extends AzureManager> { - public StorageQueueManager(Azure azure, AzureProperties azureProperties) { - super(azure, azureProperties); + public StorageQueueManager(AzureProperties azureProperties) { + super(azureProperties); } @Override diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageConnectionStringBuilder.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageConnectionStringBuilder.java index 7830b5a148e4..1ef36eed6262 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageConnectionStringBuilder.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageConnectionStringBuilder.java @@ -5,6 +5,7 @@ import com.microsoft.azure.AzureEnvironment; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -26,20 +27,33 @@ public class StorageConnectionStringBuilder { private static final String SEPARATOR = ";"; - public static String build(String accountName, String accountKey, AzureEnvironment environment, boolean - isSecureTransfer) { + private static String build(String accountName, Optional accountKey, AzureEnvironment environment, + boolean isSecureTransfer) { Map map = new HashMap<>(); map.put(DEFAULT_PROTOCOL, resolveProtocol(isSecureTransfer)); map.put(ACCOUNT_NAME, accountName); - map.put(ACCOUNT_KEY, accountKey); - // Remove starting dot since AzureEnvironment.storageEndpointSuffix() starts with dot + + if (accountKey.isPresent()) { + map.put(ACCOUNT_KEY, accountKey.get()); + } + // Remove starting dot since AzureEnvironment.storageEndpointSuffix() starts + // with dot map.put(ENDPOINT_SUFFIX, environment.storageEndpointSuffix().substring(1)); return map.entrySet().stream().map(Object::toString).collect(Collectors.joining(SEPARATOR)); } + public static String build(String accountName, String accountKey, AzureEnvironment environment, + boolean isSecureTransfer) { + return build(accountName, Optional.of(accountKey), environment, isSecureTransfer); + } + public static String build(String accountName, String accountKey, AzureEnvironment environment) { - return build(accountName, accountKey, environment, true); + return build(accountName, Optional.of(accountKey), environment, true); + } + + public static String build(String accountName, AzureEnvironment environment, boolean isSecureTransfer) { + return build(accountName, Optional.empty(), environment, isSecureTransfer); } private static String resolveProtocol(boolean isSecureTransfer) { diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageConnectionStringProvider.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageConnectionStringProvider.java index d9f5aca083e8..b38c1bdded02 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageConnectionStringProvider.java +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageConnectionStringProvider.java @@ -3,8 +3,8 @@ package com.azure.spring.cloud.context.core.storage; +import com.azure.resourcemanager.storage.models.StorageAccount; import com.microsoft.azure.AzureEnvironment; -import com.microsoft.azure.management.storage.StorageAccount; /** * Get connection string from the Azure environment for the storage account. @@ -12,7 +12,7 @@ public class StorageConnectionStringProvider { public static String getConnectionString(StorageAccount storageAccount, AzureEnvironment environment, - boolean isSecureTransfer) { + boolean isSecureTransfer) { return buildConnectionString(storageAccount, environment, isSecureTransfer); } @@ -25,14 +25,15 @@ public static String getConnectionString(String storageAccount, String accessKey } public static String getConnectionString(String storageAccount, String accessKey, AzureEnvironment environment, - boolean isSecureTransfer) { + boolean isSecureTransfer) { return StorageConnectionStringBuilder.build(storageAccount, accessKey, environment, isSecureTransfer); } private static String buildConnectionString(StorageAccount storageAccount, AzureEnvironment environment, - boolean isSecureTransfer) { - return storageAccount.getKeys().stream().findFirst().map(key -> StorageConnectionStringBuilder - .build(storageAccount.name(), key.value(), environment, isSecureTransfer)) - .orElseThrow(() -> new RuntimeException("Storage account key is empty.")); + boolean isSecureTransfer) { + return storageAccount + .getKeys().stream().findFirst().map(key -> StorageConnectionStringBuilder.build(storageAccount.name(), + key.value(), environment, isSecureTransfer)) + .orElseThrow(() -> new RuntimeException("Storage account key is empty.")); } } diff --git a/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageEndpointStringBuilder.java b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageEndpointStringBuilder.java new file mode 100644 index 000000000000..4f712b0f658c --- /dev/null +++ b/sdk/spring/azure-spring-cloud-context/src/main/java/com/azure/spring/cloud/context/core/storage/StorageEndpointStringBuilder.java @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.context.core.storage; + +import com.microsoft.azure.AzureEnvironment; + +/** + * The builder class to build the storage endpoint string. + */ +public class StorageEndpointStringBuilder { + + public static String buildBlobEndpoint(String storageAccount, AzureEnvironment azureEnvironment, + boolean isSecureTransfer) { + String scheme = isSecureTransfer ? "https://" : "http://"; + return scheme + storageAccount + ".blob" + azureEnvironment.storageEndpointSuffix(); + } + + public static String buildSharesEndpoint(String storageAccount, AzureEnvironment azureEnvironment, + boolean isSecureTransfer) { + String scheme = isSecureTransfer ? "https://" : "http://"; + return scheme + storageAccount + ".file" + azureEnvironment.storageEndpointSuffix(); + } +} diff --git a/sdk/spring/azure-spring-cloud-context/src/main/resources/META-INF/spring.factories b/sdk/spring/azure-spring-cloud-context/src/main/resources/META-INF/spring.factories index 28acdd1e1098..b1fca22272b8 100644 --- a/sdk/spring/azure-spring-cloud-context/src/main/resources/META-INF/spring.factories +++ b/sdk/spring/azure-spring-cloud-context/src/main/resources/META-INF/spring.factories @@ -1,3 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration,\ -com.azure.spring.cloud.autoconfigure.context.AzureEnvironmentAutoConfiguration +com.azure.spring.cloud.autoconfigure.context.AzureEnvironmentAutoConfiguration,\ +com.azure.spring.cloud.autoconfigure.context.AzureResourceManager20AutoConfiguration diff --git a/sdk/spring/azure-spring-cloud-storage/pom.xml b/sdk/spring/azure-spring-cloud-storage/pom.xml index 43e77a5dc2ac..81ff38ae31c1 100644 --- a/sdk/spring/azure-spring-cloud-storage/pom.xml +++ b/sdk/spring/azure-spring-cloud-storage/pom.xml @@ -26,7 +26,7 @@ com.azure azure-storage-blob - 12.8.0 + 12.8.0 @@ -50,6 +50,12 @@ true + + com.azure.resourcemanager + azure-resourcemanager-storage + 2.0.0 + + com.azure.spring diff --git a/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageAutoConfiguration.java b/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageAutoConfiguration.java index 74abbd77f193..d1dd7be50151 100644 --- a/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageAutoConfiguration.java +++ b/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageAutoConfiguration.java @@ -3,16 +3,26 @@ package com.azure.spring.cloud.autoconfigure.storage; +import com.azure.core.credential.TokenCredential; +import com.azure.core.http.HttpClient; +import com.azure.core.http.HttpPipeline; import com.azure.core.http.policy.HttpLogOptions; -import com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration; +import com.azure.core.util.logging.ClientLogger; +import com.azure.resourcemanager.AzureResourceManager; +import com.azure.resourcemanager.storage.models.StorageAccount; +import com.azure.spring.cloud.autoconfigure.context.AzureResourceManager20AutoConfiguration; import com.azure.spring.cloud.context.core.api.EnvironmentProvider; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; +import com.azure.spring.cloud.context.core.config.AzureProperties; +import com.azure.spring.cloud.context.core.impl.StorageAccountManager; import com.azure.spring.cloud.context.core.storage.StorageConnectionStringProvider; +import com.azure.spring.cloud.context.core.storage.StorageEndpointStringBuilder; import com.azure.spring.cloud.storage.AzureStorageProtocolResolver; import com.azure.spring.cloud.telemetry.TelemetryCollector; import com.azure.storage.blob.BlobServiceClientBuilder; +import com.azure.storage.common.policy.RequestRetryOptions; import com.azure.storage.file.share.ShareServiceClientBuilder; -import com.microsoft.azure.management.storage.StorageAccount; +import com.azure.storage.queue.implementation.util.BuilderHelper; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -24,6 +34,7 @@ import org.springframework.context.annotation.Import; import javax.annotation.PostConstruct; +import java.util.Collections; import static com.azure.spring.cloud.context.core.util.Constants.SPRING_CLOUD_STORAGE_BLOB_APPLICATION_ID; import static com.azure.spring.cloud.context.core.util.Constants.SPRING_CLOUD_STORAGE_FILE_SHARE_APPLICATION_ID; @@ -34,7 +45,7 @@ * @author Warren Zhu */ @Configuration -@AutoConfigureAfter(AzureContextAutoConfiguration.class) +@AutoConfigureAfter(AzureResourceManager20AutoConfiguration.class) @ConditionalOnClass({ BlobServiceClientBuilder.class, ShareServiceClientBuilder.class }) @ConditionalOnProperty(name = "spring.cloud.azure.storage.account") @EnableConfigurationProperties(AzureStorageProperties.class) @@ -43,7 +54,7 @@ public class AzureStorageAutoConfiguration { private static final String ACCOUNT_NAME = "AccountName"; @Autowired(required = false) - private ResourceManagerProvider resourceManagerProvider; + private StorageAccountManager storageAccountManager; @PostConstruct public void collectTelemetry() { @@ -53,53 +64,74 @@ public void collectTelemetry() { @Bean @ConditionalOnMissingBean public BlobServiceClientBuilder blobServiceClientBuilder(AzureStorageProperties storageProperties, - EnvironmentProvider environmentProvider) { - String connectionString; + EnvironmentProvider environmentProvider, + TokenCredential tokenCredential) { + + BlobServiceClientBuilder authenticatedClientBuilder = null; + + // Use storage credentials where provided, default identity otherwise. + if (StringUtils.isNotBlank(storageProperties.getAccessKey())) { + String connectionString; + if (storageAccountManager != null) { + StorageAccount storageAccount = storageAccountManager.getOrCreate(storageProperties.getAccount()); + connectionString = StorageConnectionStringProvider.getConnectionString(storageAccount, + environmentProvider.getEnvironment(), storageProperties.isSecureTransfer()); + } else { + connectionString = StorageConnectionStringProvider.getConnectionString(storageProperties.getAccount(), + storageProperties.getAccessKey(), environmentProvider.getEnvironment()); + TelemetryCollector.getInstance().addProperty(STORAGE, ACCOUNT_NAME, storageProperties.getAccount()); + } + authenticatedClientBuilder = new BlobServiceClientBuilder().connectionString(connectionString); - if (resourceManagerProvider != null) { - String accountName = storageProperties.getAccount(); - - StorageAccount storageAccount = resourceManagerProvider.getStorageAccountManager().getOrCreate(accountName); - connectionString = StorageConnectionStringProvider.getConnectionString(storageAccount, - environmentProvider.getEnvironment(), storageProperties.isSecureTransfer()); } else { - connectionString = StorageConnectionStringProvider - .getConnectionString(storageProperties.getAccount(), storageProperties.getAccessKey(), - environmentProvider.getEnvironment()); - TelemetryCollector.getInstance().addProperty(STORAGE, ACCOUNT_NAME, storageProperties.getAccount()); + final String endpoint = StorageEndpointStringBuilder.buildBlobEndpoint(storageProperties.getAccount(), + environmentProvider.getEnvironment(), storageProperties.isSecureTransfer()); + authenticatedClientBuilder = new BlobServiceClientBuilder().credential(tokenCredential) + .endpoint(endpoint); } - - return new BlobServiceClientBuilder().connectionString(connectionString) - .httpLogOptions(new HttpLogOptions() - .setApplicationId(SPRING_CLOUD_STORAGE_BLOB_APPLICATION_ID)); + return authenticatedClientBuilder + .httpLogOptions(new HttpLogOptions().setApplicationId(SPRING_CLOUD_STORAGE_BLOB_APPLICATION_ID)); } @Bean @ConditionalOnMissingBean public ShareServiceClientBuilder shareServiceClientBuilder(AzureStorageProperties storageProperties, - EnvironmentProvider environmentProvider) { - String connectionString; + EnvironmentProvider environmentProvider, + TokenCredential tokenCredential) { + + ShareServiceClientBuilder authenticatedClientBuilder = null; + // Use storage credentials where provided, default identity otherwise. + if (StringUtils.isNotBlank(storageProperties.getAccessKey())) { + String connectionString; + if (storageAccountManager != null) { + String accountName = storageProperties.getAccount(); + + StorageAccount storageAccount = storageAccountManager.getOrCreate(accountName); + connectionString = StorageConnectionStringProvider.getConnectionString(storageAccount, + environmentProvider.getEnvironment(), storageProperties.isSecureTransfer()); + } else { + connectionString = StorageConnectionStringProvider.getConnectionString(storageProperties.getAccount(), + storageProperties.getAccessKey(), environmentProvider.getEnvironment()); + TelemetryCollector.getInstance().addProperty(STORAGE, ACCOUNT_NAME, storageProperties.getAccount()); + } + authenticatedClientBuilder = new ShareServiceClientBuilder().connectionString(connectionString); + } else { - if (resourceManagerProvider != null) { - String accountName = storageProperties.getAccount(); + String endpoint = StorageEndpointStringBuilder.buildSharesEndpoint(storageProperties.getAccount(), + environmentProvider.getEnvironment(), storageProperties.isSecureTransfer()); - StorageAccount storageAccount = resourceManagerProvider.getStorageAccountManager().getOrCreate(accountName); + HttpPipeline pipeline = BuilderHelper.buildPipeline(null, tokenCredential, null, endpoint, + new RequestRetryOptions(), new HttpLogOptions(), HttpClient.createDefault(), + Collections.emptyList(), new com.azure.core.util.Configuration(), + new ClientLogger(this.getClass())); - connectionString = StorageConnectionStringProvider - .getConnectionString(storageAccount, environmentProvider.getEnvironment(), - storageProperties.isSecureTransfer()); - } else { - connectionString = StorageConnectionStringProvider - .getConnectionString(storageProperties.getAccount(), storageProperties.getAccessKey(), - environmentProvider.getEnvironment()); - TelemetryCollector.getInstance().addProperty(STORAGE, ACCOUNT_NAME, storageProperties.getAccount()); - } + authenticatedClientBuilder = new ShareServiceClientBuilder().pipeline(pipeline); + } - return new ShareServiceClientBuilder().connectionString(connectionString) - .httpLogOptions(new HttpLogOptions() - .setApplicationId(SPRING_CLOUD_STORAGE_FILE_SHARE_APPLICATION_ID)); + return authenticatedClientBuilder + .httpLogOptions(new HttpLogOptions().setApplicationId(SPRING_CLOUD_STORAGE_FILE_SHARE_APPLICATION_ID)); } @Configuration @@ -107,4 +139,11 @@ public ShareServiceClientBuilder shareServiceClientBuilder(AzureStoragePropertie @Import(AzureStorageProtocolResolver.class) static class StorageResourceConfiguration { } + + @Bean + @ConditionalOnMissingBean + public StorageAccountManager storageAccountManager(AzureResourceManager azureResourceManagement, + AzureProperties azureProperties) { + return new StorageAccountManager(azureResourceManagement, azureProperties); + } } diff --git a/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageProperties.java b/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageProperties.java index 55c25d447352..fa9e39e92b27 100644 --- a/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageProperties.java +++ b/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageProperties.java @@ -27,6 +27,8 @@ public class AzureStorageProperties { * Either accessKey or credentialFilePath should be provided */ private String accessKey; + + private String resourceGroup; public String getAccount() { return account; @@ -51,4 +53,12 @@ public boolean isSecureTransfer() { public void setSecureTransfer(boolean secureTransfer) { this.secureTransfer = secureTransfer; } + + public String getResourceGroup() { + return resourceGroup; + } + + public void setResourceGroup(String resourceGroup) { + this.resourceGroup = resourceGroup; + } } diff --git a/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageQueueAutoConfiguration.java b/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageQueueAutoConfiguration.java index bd29ecef7443..3a534efd0ace 100644 --- a/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageQueueAutoConfiguration.java +++ b/sdk/spring/azure-spring-cloud-storage/src/main/java/com/azure/spring/cloud/autoconfigure/storage/AzureStorageQueueAutoConfiguration.java @@ -3,10 +3,11 @@ package com.azure.spring.cloud.autoconfigure.storage; +import com.azure.resourcemanager.storage.models.StorageAccount; import com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration; import com.azure.spring.cloud.autoconfigure.context.AzureEnvironmentAutoConfiguration; import com.azure.spring.cloud.context.core.api.EnvironmentProvider; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; +import com.azure.spring.cloud.context.core.impl.StorageAccountManager; import com.azure.spring.cloud.context.core.storage.StorageConnectionStringProvider; import com.azure.spring.cloud.telemetry.TelemetryCollector; import com.azure.spring.integration.storage.queue.StorageQueueOperation; @@ -14,8 +15,6 @@ import com.azure.spring.integration.storage.queue.factory.DefaultStorageQueueClientFactory; import com.azure.spring.integration.storage.queue.factory.StorageQueueClientFactory; import com.azure.storage.queue.QueueServiceClient; -import com.microsoft.azure.management.storage.StorageAccount; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -30,17 +29,15 @@ * Auto-configuration class for Azure Storage Queue. */ @Configuration -@AutoConfigureAfter({AzureContextAutoConfiguration.class, AzureEnvironmentAutoConfiguration.class}) -@ConditionalOnClass({QueueServiceClient.class, StorageQueueClientFactory.class}) +@AutoConfigureAfter({ AzureContextAutoConfiguration.class, AzureEnvironmentAutoConfiguration.class, + AzureStorageAutoConfiguration.class }) +@ConditionalOnClass({ QueueServiceClient.class, StorageQueueClientFactory.class }) @ConditionalOnProperty(name = "spring.cloud.azure.storage.account") @EnableConfigurationProperties(AzureStorageProperties.class) public class AzureStorageQueueAutoConfiguration { private static final String STORAGE_QUEUE = "StorageQueue"; - @Autowired(required = false) - private ResourceManagerProvider resourceManagerProvider; - @PostConstruct public void collectTelemetry() { TelemetryCollector.getInstance().addService(STORAGE_QUEUE); @@ -49,23 +46,17 @@ public void collectTelemetry() { @Bean @ConditionalOnMissingBean StorageQueueClientFactory storageQueueClientFactory(AzureStorageProperties storageProperties, + StorageAccountManager storageAccountManager, EnvironmentProvider environmentProvider) { String connectionString; - if (resourceManagerProvider != null) { - String accountName = storageProperties.getAccount(); + String accountName = storageProperties.getAccount(); - StorageAccount storageAccount = resourceManagerProvider.getStorageAccountManager().getOrCreate(accountName); + StorageAccount storageAccount = storageAccountManager.getOrCreate(accountName); - connectionString = StorageConnectionStringProvider - .getConnectionString(storageAccount, environmentProvider.getEnvironment(), - storageProperties.isSecureTransfer()); - } else { - connectionString = StorageConnectionStringProvider - .getConnectionString(storageProperties.getAccount(), storageProperties.getAccessKey(), - environmentProvider.getEnvironment()); - } + connectionString = StorageConnectionStringProvider.getConnectionString(storageAccount, + environmentProvider.getEnvironment(), storageProperties.isSecureTransfer()); return new DefaultStorageQueueClientFactory(connectionString); } diff --git a/sdk/spring/azure-spring-cloud-storage/src/test/java/com/azure/spring/cloud/autoconfigure/storage/actuator/BlobStorageHealthIndicatorTest.java b/sdk/spring/azure-spring-cloud-storage/src/test/java/com/azure/spring/cloud/autoconfigure/storage/actuator/BlobStorageHealthIndicatorTest.java index 5252b719756b..6a24e0587720 100644 --- a/sdk/spring/azure-spring-cloud-storage/src/test/java/com/azure/spring/cloud/autoconfigure/storage/actuator/BlobStorageHealthIndicatorTest.java +++ b/sdk/spring/azure-spring-cloud-storage/src/test/java/com/azure/spring/cloud/autoconfigure/storage/actuator/BlobStorageHealthIndicatorTest.java @@ -3,10 +3,15 @@ package com.azure.spring.cloud.autoconfigure.storage.actuator; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - +import com.azure.core.credential.TokenCredential; +import com.azure.spring.cloud.autoconfigure.context.AzureEnvironmentAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.context.AzureResourceManager20AutoConfiguration; import com.azure.spring.cloud.autoconfigure.storage.AzureStorageAutoConfiguration; +import com.azure.storage.blob.BlobServiceAsyncClient; +import com.azure.storage.blob.BlobServiceClientBuilder; +import com.azure.storage.blob.models.AccountKind; +import com.azure.storage.blob.models.SkuName; +import com.azure.storage.blob.models.StorageAccountInfo; import org.apache.http.HttpException; import org.junit.Assert; import org.junit.Test; @@ -16,16 +21,11 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; - -import com.azure.storage.blob.BlobServiceAsyncClient; -import com.azure.storage.blob.BlobServiceClientBuilder; -import com.azure.storage.blob.models.AccountKind; -import com.azure.storage.blob.models.SkuName; -import com.azure.storage.blob.models.StorageAccountInfo; -import com.azure.spring.cloud.autoconfigure.context.AzureEnvironmentAutoConfiguration; - import reactor.core.publisher.Mono; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public class BlobStorageHealthIndicatorTest { private static final String MOCK_URL = "https://example.org/bigly_fake_url"; @@ -46,12 +46,16 @@ public void testWithStorageConfigurationWithConnectionUp() { ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withAllowBeanDefinitionOverriding(true) .withConfiguration(AutoConfigurations.of(AzureEnvironmentAutoConfiguration.class, - AzureStorageAutoConfiguration.class, AzureStorageActuatorAutoConfiguration.class)) + MockTokenCredentialConfiguration.class, + AzureStorageAutoConfiguration.class, + AzureStorageActuatorAutoConfiguration.class)) .withUserConfiguration(TestConfigurationConnectionUp.class) .withPropertyValues("spring.cloud.azure.storage.account=acc1") + .withPropertyValues("spring.cloud.azure.resource-group=myrg") .withBean(BlobStorageHealthIndicator.class); contextRunner.run(context -> { - Health health = context.getBean("blobStorageHealthIndicator", BlobStorageHealthIndicator.class).getHealth(true); + Health health = context.getBean("blobStorageHealthIndicator", BlobStorageHealthIndicator.class) + .getHealth(true); Assert.assertEquals(Status.UP, health.getStatus()); Assert.assertEquals(MOCK_URL, health.getDetails().get(AzureStorageActuatorConstants.URL_FIELD)); }); @@ -61,13 +65,19 @@ public void testWithStorageConfigurationWithConnectionUp() { public void testWithStorageConfigurationWithConnectionDown() { ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withAllowBeanDefinitionOverriding(true) - .withConfiguration(AutoConfigurations.of(AzureEnvironmentAutoConfiguration.class, - AzureStorageAutoConfiguration.class, AzureStorageActuatorAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of( + AzureEnvironmentAutoConfiguration.class, + MockTokenCredentialConfiguration.class, + AzureResourceManager20AutoConfiguration.class, + AzureStorageAutoConfiguration.class, + AzureStorageActuatorAutoConfiguration.class)) .withUserConfiguration(TestConfigurationConnectionDown.class) - .withPropertyValues("spring.cloud.azure.storage.account=acc1") + .withPropertyValues("spring.cloud" + + ".azure.storage.account=acc1") .withBean(BlobStorageHealthIndicator.class); contextRunner.run(context -> { - Health health = context.getBean("blobStorageHealthIndicator", BlobStorageHealthIndicator.class).getHealth(true); + Health health = context.getBean("blobStorageHealthIndicator", BlobStorageHealthIndicator.class) + .getHealth(true); Assert.assertEquals(Status.DOWN, health.getStatus()); Assert.assertEquals(MOCK_URL, health.getDetails().get(AzureStorageActuatorConstants.URL_FIELD)); }); @@ -82,7 +92,7 @@ BlobServiceClientBuilder blobServiceClientBuilder() { BlobServiceAsyncClient mockAsyncClient = mock(BlobServiceAsyncClient.class); when(mockAsyncClient.getAccountUrl()).thenReturn(MOCK_URL); when(mockAsyncClient.getAccountInfo()) - .thenReturn(Mono.just(new StorageAccountInfo(SkuName.STANDARD_LRS, AccountKind.BLOB_STORAGE))); + .thenReturn(Mono.just(new StorageAccountInfo(SkuName.STANDARD_LRS, AccountKind.BLOB_STORAGE))); when(mockClientBuilder.buildAsyncClient()).thenReturn(mockAsyncClient); return mockClientBuilder; @@ -98,10 +108,19 @@ BlobServiceClientBuilder blobServiceClientBuilder() { BlobServiceAsyncClient mockAsyncClient = mock(BlobServiceAsyncClient.class); when(mockAsyncClient.getAccountUrl()).thenReturn(MOCK_URL); when(mockAsyncClient.getAccountInfo()) - .thenReturn(Mono.error(new HttpException("The gremlins have cut the cable."))); + .thenReturn(Mono.error(new HttpException("The gremlins have cut the cable."))); when(mockClientBuilder.buildAsyncClient()).thenReturn(mockAsyncClient); return mockClientBuilder; } } + + @Configuration + static class MockTokenCredentialConfiguration { + + @Bean + TokenCredential tokenCredential() { + return mock(TokenCredential.class); + } + } } diff --git a/sdk/spring/azure-spring-cloud-storage/src/test/java/com/azure/spring/cloud/autoconfigure/storage/actuator/FileStorageHealthIndicatorTest.java b/sdk/spring/azure-spring-cloud-storage/src/test/java/com/azure/spring/cloud/autoconfigure/storage/actuator/FileStorageHealthIndicatorTest.java index cf1ca9c3841e..e6e59016fe6c 100644 --- a/sdk/spring/azure-spring-cloud-storage/src/test/java/com/azure/spring/cloud/autoconfigure/storage/actuator/FileStorageHealthIndicatorTest.java +++ b/sdk/spring/azure-spring-cloud-storage/src/test/java/com/azure/spring/cloud/autoconfigure/storage/actuator/FileStorageHealthIndicatorTest.java @@ -18,6 +18,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import com.azure.core.credential.TokenCredential; import com.azure.core.http.rest.Response; import com.azure.storage.file.share.ShareServiceAsyncClient; import com.azure.storage.file.share.ShareServiceClientBuilder; @@ -31,10 +32,9 @@ public class FileStorageHealthIndicatorTest { @Test(expected = IllegalStateException.class) public void testWithNoStorageConfiguration() { - ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withAllowBeanDefinitionOverriding(true) - .withBean(ShareServiceClientBuilder.class) - .withConfiguration(AutoConfigurations.of(AzureStorageActuatorAutoConfiguration.class)); + ApplicationContextRunner contextRunner = new ApplicationContextRunner().withAllowBeanDefinitionOverriding(true) + .withBean(ShareServiceClientBuilder.class) + .withConfiguration(AutoConfigurations.of(AzureStorageActuatorAutoConfiguration.class)); contextRunner.withBean(FileStorageHealthIndicator.class).run(context -> { context.getBean(FileStorageHealthIndicator.class).getHealth(true); @@ -43,13 +43,13 @@ public void testWithNoStorageConfiguration() { @Test public void testWithStorageConfigurationWithConnectionUp() { - ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withAllowBeanDefinitionOverriding(true) - .withConfiguration(AutoConfigurations.of(AzureEnvironmentAutoConfiguration.class, - AzureStorageAutoConfiguration.class, AzureStorageActuatorAutoConfiguration.class)) - .withUserConfiguration(TestConfigurationConnectionUp.class) - .withPropertyValues("spring.cloud.azure.storage.account=acc1") - .withBean(FileStorageHealthIndicator.class); + ApplicationContextRunner contextRunner = new ApplicationContextRunner().withAllowBeanDefinitionOverriding(true) + .withConfiguration(AutoConfigurations.of(AzureEnvironmentAutoConfiguration.class, + MockTokenCredentialConfiguration.class, AzureStorageAutoConfiguration.class, + AzureStorageActuatorAutoConfiguration.class)) + .withUserConfiguration(TestConfigurationConnectionUp.class) + .withPropertyValues("spring.cloud.azure.storage.account=acc1") + .withBean(FileStorageHealthIndicator.class); contextRunner.run(context -> { Health health = context.getBean(FileStorageHealthIndicator.class).getHealth(true); Assert.assertEquals(Status.UP, health.getStatus()); @@ -59,13 +59,13 @@ public void testWithStorageConfigurationWithConnectionUp() { @Test public void testWithStorageConfigurationWithConnectionDown() { - ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withAllowBeanDefinitionOverriding(true) - .withConfiguration(AutoConfigurations.of(AzureEnvironmentAutoConfiguration.class, - AzureStorageAutoConfiguration.class, AzureStorageActuatorAutoConfiguration.class)) - .withUserConfiguration(TestConfigurationConnectionDown.class) - .withPropertyValues("spring.cloud.azure.storage.account=acc1") - .withBean(BlobStorageHealthIndicator.class); + ApplicationContextRunner contextRunner = new ApplicationContextRunner().withAllowBeanDefinitionOverriding(true) + .withConfiguration(AutoConfigurations.of(AzureEnvironmentAutoConfiguration.class, + MockTokenCredentialConfiguration.class, AzureStorageAutoConfiguration.class, + AzureStorageActuatorAutoConfiguration.class)) + .withUserConfiguration(TestConfigurationConnectionDown.class) + .withPropertyValues("spring.cloud.azure.storage.account=acc1") + .withBean(BlobStorageHealthIndicator.class); contextRunner.run(context -> { Health health = context.getBean(FileStorageHealthIndicator.class).getHealth(true); Assert.assertEquals(Status.DOWN, health.getStatus()); @@ -80,8 +80,8 @@ static class TestConfigurationConnectionUp { ShareServiceClientBuilder shareServiceClientBuilder() { ShareServiceClientBuilder mockClientBuilder = mock(ShareServiceClientBuilder.class); ShareServiceAsyncClient mockAsyncClient = mock(ShareServiceAsyncClient.class); - @SuppressWarnings("unchecked") + Response mockResponse = (Response) Mockito .mock(Response.class); @@ -109,4 +109,13 @@ ShareServiceClientBuilder shareServiceClientBuilder() { } } + + @Configuration + static class MockTokenCredentialConfiguration { + + @Bean + TokenCredential tokenCredential() { + return mock(TokenCredential.class); + } + } } diff --git a/sdk/spring/azure-spring-cloud-stream-binder-eventhubs/src/main/java/com/azure/spring/eventhub/stream/binder/config/EventHubBinderConfiguration.java b/sdk/spring/azure-spring-cloud-stream-binder-eventhubs/src/main/java/com/azure/spring/eventhub/stream/binder/config/EventHubBinderConfiguration.java index e90ce0e608e5..88b319b743dc 100644 --- a/sdk/spring/azure-spring-cloud-stream-binder-eventhubs/src/main/java/com/azure/spring/eventhub/stream/binder/config/EventHubBinderConfiguration.java +++ b/sdk/spring/azure-spring-cloud-stream-binder-eventhubs/src/main/java/com/azure/spring/eventhub/stream/binder/config/EventHubBinderConfiguration.java @@ -3,41 +3,53 @@ package com.azure.spring.eventhub.stream.binder.config; -import com.azure.spring.eventhub.stream.binder.properties.EventHubExtendedBindingProperties; -import com.azure.spring.eventhub.stream.binder.EventHubMessageChannelBinder; -import com.azure.spring.eventhub.stream.binder.provisioning.EventHubChannelProvisioner; -import com.azure.spring.eventhub.stream.binder.provisioning.EventHubChannelResourceManagerProvisioner; -import com.azure.spring.cloud.autoconfigure.context.AzureEnvironmentAutoConfiguration; -import com.azure.spring.cloud.autoconfigure.eventhub.AzureEventHubAutoConfiguration; -import com.azure.spring.cloud.autoconfigure.eventhub.AzureEventHubProperties; -import com.azure.spring.cloud.autoconfigure.eventhub.EventHubUtils; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; -import com.azure.spring.cloud.telemetry.TelemetryCollector; -import com.azure.spring.integration.eventhub.api.EventHubOperation; +import javax.annotation.PostConstruct; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.stream.binder.Binder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import javax.annotation.PostConstruct; +import com.azure.spring.cloud.autoconfigure.context.AzureEnvironmentAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.eventhub.AzureEventHubAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.eventhub.AzureEventHubProperties; +import com.azure.spring.cloud.autoconfigure.eventhub.EventHubUtils; +import com.azure.spring.cloud.context.core.config.AzureProperties; +import com.azure.spring.cloud.context.core.impl.EventHubConsumerGroupManager; +import com.azure.spring.cloud.context.core.impl.EventHubManager; +import com.azure.spring.cloud.context.core.impl.EventHubNamespaceManager; +import com.azure.spring.cloud.telemetry.TelemetryCollector; +import com.azure.spring.eventhub.stream.binder.EventHubMessageChannelBinder; +import com.azure.spring.eventhub.stream.binder.properties.EventHubExtendedBindingProperties; +import com.azure.spring.eventhub.stream.binder.provisioning.EventHubChannelProvisioner; +import com.azure.spring.eventhub.stream.binder.provisioning.EventHubChannelResourceManagerProvisioner; +import com.azure.spring.integration.eventhub.api.EventHubOperation; +import com.microsoft.azure.management.Azure; /** * @author Warren Zhu */ @Configuration @ConditionalOnMissingBean(Binder.class) -@Import({AzureEventHubAutoConfiguration.class, AzureEnvironmentAutoConfiguration.class}) -@EnableConfigurationProperties({AzureEventHubProperties.class, EventHubExtendedBindingProperties.class}) +@Import({ AzureEventHubAutoConfiguration.class, AzureEnvironmentAutoConfiguration.class }) +@EnableConfigurationProperties({ AzureEventHubProperties.class, EventHubExtendedBindingProperties.class }) public class EventHubBinderConfiguration { private static final String EVENT_HUB_BINDER = "EventHubBinder"; private static final String NAMESPACE = "Namespace"; @Autowired(required = false) - private ResourceManagerProvider resourceManagerProvider; + private EventHubNamespaceManager eventHubNamespaceManager; + + @Autowired(required = false) + private EventHubManager eventHubManager; + + @Autowired(required = false) + private EventHubConsumerGroupManager eventHubConsumerGroupManager; @PostConstruct public void collectTelemetry() { @@ -47,12 +59,16 @@ public void collectTelemetry() { @Bean @ConditionalOnMissingBean public EventHubChannelProvisioner eventHubChannelProvisioner(AzureEventHubProperties eventHubProperties) { - if (resourceManagerProvider != null) { - return new EventHubChannelResourceManagerProvisioner(resourceManagerProvider, - eventHubProperties.getNamespace()); + // TODO: With the previous ResourceManagerProvider architecture, eventHubManager + // and eventHubConsumerGroup manager were created unconditionally. + // Now, they are not created at all. Should they be? + if (eventHubNamespaceManager != null && eventHubManager != null && eventHubConsumerGroupManager != null) { + return new EventHubChannelResourceManagerProvisioner(eventHubNamespaceManager, eventHubManager, + eventHubConsumerGroupManager, eventHubProperties.getNamespace()); } else { - TelemetryCollector.getInstance().addProperty(EVENT_HUB_BINDER, NAMESPACE, - EventHubUtils.getNamespace(eventHubProperties.getConnectionString())); + String namespace = eventHubProperties.getNamespace() != null ? eventHubProperties.getNamespace() + : EventHubUtils.getNamespace(eventHubProperties.getConnectionString()); + TelemetryCollector.getInstance().addProperty(EVENT_HUB_BINDER, NAMESPACE, namespace); } return new EventHubChannelProvisioner(); @@ -62,9 +78,23 @@ public EventHubChannelProvisioner eventHubChannelProvisioner(AzureEventHubProper @ConditionalOnMissingBean public EventHubMessageChannelBinder eventHubBinder(EventHubChannelProvisioner eventHubChannelProvisioner, EventHubOperation eventHubOperation, EventHubExtendedBindingProperties bindingProperties) { - EventHubMessageChannelBinder binder = - new EventHubMessageChannelBinder(null, eventHubChannelProvisioner, eventHubOperation); + EventHubMessageChannelBinder binder = new EventHubMessageChannelBinder(null, eventHubChannelProvisioner, + eventHubOperation); binder.setBindingProperties(bindingProperties); return binder; } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty("spring.cloud.azure.resource-group") + public EventHubManager eventHubManager(Azure azure, AzureProperties azureProperties) { + return new EventHubManager(azure, azureProperties); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty("spring.cloud.azure.resource-group") + public EventHubConsumerGroupManager eventHubConsumerGroupManager(Azure azure, AzureProperties azureProperties) { + return new EventHubConsumerGroupManager(azure, azureProperties); + } } diff --git a/sdk/spring/azure-spring-cloud-stream-binder-eventhubs/src/main/java/com/azure/spring/eventhub/stream/binder/provisioning/EventHubChannelResourceManagerProvisioner.java b/sdk/spring/azure-spring-cloud-stream-binder-eventhubs/src/main/java/com/azure/spring/eventhub/stream/binder/provisioning/EventHubChannelResourceManagerProvisioner.java index e9e1543fd9f9..de416b40b6f1 100644 --- a/sdk/spring/azure-spring-cloud-stream-binder-eventhubs/src/main/java/com/azure/spring/eventhub/stream/binder/provisioning/EventHubChannelResourceManagerProvisioner.java +++ b/sdk/spring/azure-spring-cloud-stream-binder-eventhubs/src/main/java/com/azure/spring/eventhub/stream/binder/provisioning/EventHubChannelResourceManagerProvisioner.java @@ -3,10 +3,12 @@ package com.azure.spring.eventhub.stream.binder.provisioning; +import com.azure.spring.cloud.context.core.impl.EventHubConsumerGroupManager; +import com.azure.spring.cloud.context.core.impl.EventHubManager; +import com.azure.spring.cloud.context.core.impl.EventHubNamespaceManager; +import com.azure.spring.cloud.context.core.util.Tuple; import com.microsoft.azure.management.eventhub.EventHub; import com.microsoft.azure.management.eventhub.EventHubNamespace; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; -import com.azure.spring.cloud.context.core.util.Tuple; import org.springframework.cloud.stream.provisioning.ProvisioningException; import org.springframework.lang.NonNull; import org.springframework.util.Assert; @@ -16,35 +18,38 @@ */ public class EventHubChannelResourceManagerProvisioner extends EventHubChannelProvisioner { private final String namespace; - private final ResourceManagerProvider resourceManagerProvider; + private final EventHubNamespaceManager eventHubNamespaceManager; + private final EventHubManager eventHubManager; + private final EventHubConsumerGroupManager eventHubConsumerGroupManager; - public EventHubChannelResourceManagerProvisioner(@NonNull ResourceManagerProvider resourceManagerProvider, - @NonNull String namespace) { + public EventHubChannelResourceManagerProvisioner(@NonNull EventHubNamespaceManager eventHubNamespaceManager, + @NonNull EventHubManager eventHubManager, + @NonNull EventHubConsumerGroupManager eventHubConsumerGroupManager, @NonNull String namespace) { Assert.hasText(namespace, "The namespace can't be null or empty"); this.namespace = namespace; - this.resourceManagerProvider = resourceManagerProvider; + this.eventHubNamespaceManager = eventHubNamespaceManager; + this.eventHubManager = eventHubManager; + this.eventHubConsumerGroupManager = eventHubConsumerGroupManager; } @Override protected void validateOrCreateForConsumer(String name, String group) { - EventHubNamespace eventHubNamespace = - this.resourceManagerProvider.getEventHubNamespaceManager().getOrCreate(namespace); - EventHub eventHub = this.resourceManagerProvider.getEventHubManager().get(Tuple.of(eventHubNamespace, name)); + EventHubNamespace eventHubNamespace = eventHubNamespaceManager.getOrCreate(namespace); + // If the consumer is created before the producer, we need to create the event + // hub with it. + // Otherwise, this method will fail and the startup of a distributed + // application, where no order of operations can be imposed, will fail with it. + EventHub eventHub = eventHubManager.getOrCreate(Tuple.of(eventHubNamespace, name)); if (eventHub == null) { throw new ProvisioningException( - String.format("Event hub with name '%s' in namespace '%s' not existed", name, namespace)); + String.format("Event hub with name '%s' in namespace '%s' could not be created", name, namespace)); } - - this.resourceManagerProvider.getEventHubConsumerGroupManager().getOrCreate(Tuple.of(eventHub, group)); + eventHubConsumerGroupManager.getOrCreate(Tuple.of(eventHub, group)); } @Override protected void validateOrCreateForProducer(String name) { - if (resourceManagerProvider == null) { - return; - } - EventHubNamespace eventHubNamespace = - this.resourceManagerProvider.getEventHubNamespaceManager().getOrCreate(namespace); - this.resourceManagerProvider.getEventHubManager().getOrCreate(Tuple.of(eventHubNamespace, name)); + EventHubNamespace eventHubNamespace = eventHubNamespaceManager.getOrCreate(namespace); + eventHubManager.getOrCreate(Tuple.of(eventHubNamespace, name)); } } diff --git a/sdk/spring/azure-spring-cloud-stream-binder-servicebus-queue/src/main/java/com/azure/spring/servicebus/stream/binder/config/ServiceBusQueueBinderConfiguration.java b/sdk/spring/azure-spring-cloud-stream-binder-servicebus-queue/src/main/java/com/azure/spring/servicebus/stream/binder/config/ServiceBusQueueBinderConfiguration.java index 66be199de016..b59954752ea9 100644 --- a/sdk/spring/azure-spring-cloud-stream-binder-servicebus-queue/src/main/java/com/azure/spring/servicebus/stream/binder/config/ServiceBusQueueBinderConfiguration.java +++ b/sdk/spring/azure-spring-cloud-stream-binder-servicebus-queue/src/main/java/com/azure/spring/servicebus/stream/binder/config/ServiceBusQueueBinderConfiguration.java @@ -3,19 +3,20 @@ package com.azure.spring.servicebus.stream.binder.config; -import com.azure.spring.servicebus.stream.binder.properties.ServiceBusQueueExtendedBindingProperties; -import com.azure.spring.servicebus.stream.binder.provisioning.ServiceBusChannelProvisioner; -import com.azure.spring.servicebus.stream.binder.provisioning.ServiceBusQueueChannelResourceManagerProvisioner; -import com.azure.spring.servicebus.stream.binder.ServiceBusQueueMessageChannelBinder; import com.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration; import com.azure.spring.cloud.autoconfigure.context.AzureEnvironmentAutoConfiguration; import com.azure.spring.cloud.autoconfigure.servicebus.AzureServiceBusAutoConfiguration; import com.azure.spring.cloud.autoconfigure.servicebus.AzureServiceBusProperties; import com.azure.spring.cloud.autoconfigure.servicebus.AzureServiceBusQueueAutoConfiguration; import com.azure.spring.cloud.autoconfigure.servicebus.ServiceBusUtils; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; +import com.azure.spring.cloud.context.core.impl.ServiceBusNamespaceManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusQueueManager; import com.azure.spring.cloud.telemetry.TelemetryCollector; import com.azure.spring.integration.servicebus.queue.ServiceBusQueueOperation; +import com.azure.spring.servicebus.stream.binder.ServiceBusQueueMessageChannelBinder; +import com.azure.spring.servicebus.stream.binder.properties.ServiceBusQueueExtendedBindingProperties; +import com.azure.spring.servicebus.stream.binder.provisioning.ServiceBusChannelProvisioner; +import com.azure.spring.servicebus.stream.binder.provisioning.ServiceBusQueueChannelResourceManagerProvisioner; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -32,16 +33,19 @@ */ @Configuration @ConditionalOnMissingBean(Binder.class) -@Import({AzureServiceBusQueueAutoConfiguration.class, AzureEnvironmentAutoConfiguration.class, - AzureContextAutoConfiguration.class, AzureServiceBusAutoConfiguration.class}) -@EnableConfigurationProperties({AzureServiceBusProperties.class, ServiceBusQueueExtendedBindingProperties.class}) +@Import({ AzureServiceBusQueueAutoConfiguration.class, AzureEnvironmentAutoConfiguration.class, + AzureContextAutoConfiguration.class, AzureServiceBusAutoConfiguration.class }) +@EnableConfigurationProperties({ AzureServiceBusProperties.class, ServiceBusQueueExtendedBindingProperties.class }) public class ServiceBusQueueBinderConfiguration { private static final String SERVICE_BUS_QUEUE_BINDER = "ServiceBusQueueBinder"; private static final String NAMESPACE = "Namespace"; @Autowired(required = false) - private ResourceManagerProvider resourceManagerProvider; + private ServiceBusNamespaceManager serviceBusNamespaceManager; + + @Autowired(required = false) + private ServiceBusQueueManager serviceBusQueueManager; @PostConstruct public void collectTelemetry() { @@ -49,31 +53,31 @@ public void collectTelemetry() { } @Bean - @ConditionalOnBean(ResourceManagerProvider.class) + @ConditionalOnBean({ ServiceBusNamespaceManager.class, ServiceBusQueueManager.class }) @ConditionalOnMissingBean public ServiceBusChannelProvisioner serviceBusChannelProvisioner(AzureServiceBusProperties serviceBusProperties) { - if (this.resourceManagerProvider != null) { - return new ServiceBusQueueChannelResourceManagerProvisioner(resourceManagerProvider, - serviceBusProperties.getNamespace()); + if (this.serviceBusNamespaceManager != null && serviceBusQueueManager != null) { + return new ServiceBusQueueChannelResourceManagerProvisioner(serviceBusNamespaceManager, + serviceBusQueueManager, serviceBusProperties.getNamespace()); } else { TelemetryCollector.getInstance().addProperty(SERVICE_BUS_QUEUE_BINDER, NAMESPACE, - ServiceBusUtils.getNamespace(serviceBusProperties.getConnectionString())); + ServiceBusUtils.getNamespace(serviceBusProperties.getConnectionString())); } return new ServiceBusChannelProvisioner(); } @Bean - @ConditionalOnMissingBean({ResourceManagerProvider.class, ServiceBusChannelProvisioner.class}) + @ConditionalOnMissingBean(ServiceBusChannelProvisioner.class) public ServiceBusChannelProvisioner serviceBusChannelProvisionerWithResourceManagerProvider() { return new ServiceBusChannelProvisioner(); } @Bean public ServiceBusQueueMessageChannelBinder serviceBusQueueBinder( - ServiceBusChannelProvisioner queueChannelProvisioner, ServiceBusQueueOperation serviceBusQueueOperation, - ServiceBusQueueExtendedBindingProperties bindingProperties) { - ServiceBusQueueMessageChannelBinder binder = - new ServiceBusQueueMessageChannelBinder(null, queueChannelProvisioner, serviceBusQueueOperation); + ServiceBusChannelProvisioner queueChannelProvisioner, ServiceBusQueueOperation serviceBusQueueOperation, + ServiceBusQueueExtendedBindingProperties bindingProperties) { + ServiceBusQueueMessageChannelBinder binder = new ServiceBusQueueMessageChannelBinder(null, + queueChannelProvisioner, serviceBusQueueOperation); binder.setBindingProperties(bindingProperties); return binder; } diff --git a/sdk/spring/azure-spring-cloud-stream-binder-servicebus-queue/src/main/java/com/azure/spring/servicebus/stream/binder/provisioning/ServiceBusQueueChannelResourceManagerProvisioner.java b/sdk/spring/azure-spring-cloud-stream-binder-servicebus-queue/src/main/java/com/azure/spring/servicebus/stream/binder/provisioning/ServiceBusQueueChannelResourceManagerProvisioner.java index 9d196111bd40..7fbd233c36c3 100644 --- a/sdk/spring/azure-spring-cloud-stream-binder-servicebus-queue/src/main/java/com/azure/spring/servicebus/stream/binder/provisioning/ServiceBusQueueChannelResourceManagerProvisioner.java +++ b/sdk/spring/azure-spring-cloud-stream-binder-servicebus-queue/src/main/java/com/azure/spring/servicebus/stream/binder/provisioning/ServiceBusQueueChannelResourceManagerProvisioner.java @@ -3,10 +3,11 @@ package com.azure.spring.servicebus.stream.binder.provisioning; +import com.azure.spring.cloud.context.core.impl.ServiceBusNamespaceManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusQueueManager; +import com.azure.spring.cloud.context.core.util.Tuple; import com.microsoft.azure.management.servicebus.Queue; import com.microsoft.azure.management.servicebus.ServiceBusNamespace; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; -import com.azure.spring.cloud.context.core.util.Tuple; import org.springframework.cloud.stream.provisioning.ProvisioningException; import org.springframework.lang.NonNull; import org.springframework.util.Assert; @@ -16,21 +17,23 @@ */ public class ServiceBusQueueChannelResourceManagerProvisioner extends ServiceBusChannelProvisioner { - private final ResourceManagerProvider resourceManagerProvider; private final String namespace; + private final ServiceBusNamespaceManager serviceBusNamespaceManager; + private final ServiceBusQueueManager serviceBusQueueManager; - public ServiceBusQueueChannelResourceManagerProvisioner(@NonNull ResourceManagerProvider resourceManagerProvider, - @NonNull String namespace) { + public ServiceBusQueueChannelResourceManagerProvisioner( + @NonNull ServiceBusNamespaceManager serviceBusNamespaceManager, + @NonNull ServiceBusQueueManager serviceBusQueueManager, @NonNull String namespace) { Assert.hasText(namespace, "The namespace can't be null or empty"); - this.resourceManagerProvider = resourceManagerProvider; + this.serviceBusNamespaceManager = serviceBusNamespaceManager; + this.serviceBusQueueManager = serviceBusQueueManager; this.namespace = namespace; } @Override protected void validateOrCreateForConsumer(String name, String group) { - ServiceBusNamespace namespace = - this.resourceManagerProvider.getServiceBusNamespaceManager().getOrCreate(this.namespace); - Queue queue = this.resourceManagerProvider.getServiceBusQueueManager().getOrCreate(Tuple.of(namespace, name)); + ServiceBusNamespace namespace = serviceBusNamespaceManager.getOrCreate(this.namespace); + Queue queue = serviceBusQueueManager.getOrCreate(Tuple.of(namespace, name)); if (queue == null) { throw new ProvisioningException( String.format("Event hub with name '%s' in namespace '%s' not existed", name, namespace)); @@ -39,8 +42,7 @@ protected void validateOrCreateForConsumer(String name, String group) { @Override protected void validateOrCreateForProducer(String name) { - ServiceBusNamespace namespace = - this.resourceManagerProvider.getServiceBusNamespaceManager().getOrCreate(this.namespace); - this.resourceManagerProvider.getServiceBusQueueManager().getOrCreate(Tuple.of(namespace, name)); + ServiceBusNamespace namespace = serviceBusNamespaceManager.getOrCreate(this.namespace); + serviceBusQueueManager.getOrCreate(Tuple.of(namespace, name)); } } diff --git a/sdk/spring/azure-spring-cloud-stream-binder-servicebus-topic/src/main/java/com/azure/spring/servicebus/stream/binder/config/ServiceBusTopicBinderConfiguration.java b/sdk/spring/azure-spring-cloud-stream-binder-servicebus-topic/src/main/java/com/azure/spring/servicebus/stream/binder/config/ServiceBusTopicBinderConfiguration.java index e175430fa437..2b54e8d91601 100644 --- a/sdk/spring/azure-spring-cloud-stream-binder-servicebus-topic/src/main/java/com/azure/spring/servicebus/stream/binder/config/ServiceBusTopicBinderConfiguration.java +++ b/sdk/spring/azure-spring-cloud-stream-binder-servicebus-topic/src/main/java/com/azure/spring/servicebus/stream/binder/config/ServiceBusTopicBinderConfiguration.java @@ -3,17 +3,19 @@ package com.azure.spring.servicebus.stream.binder.config; -import com.azure.spring.servicebus.stream.binder.properties.ServiceBusTopicExtendedBindingProperties; -import com.azure.spring.servicebus.stream.binder.provisioning.ServiceBusChannelProvisioner; -import com.azure.spring.servicebus.stream.binder.provisioning.ServiceBusTopicChannelResourceManagerProvisioner; -import com.azure.spring.servicebus.stream.binder.ServiceBusTopicMessageChannelBinder; import com.azure.spring.cloud.autoconfigure.context.AzureEnvironmentAutoConfiguration; import com.azure.spring.cloud.autoconfigure.servicebus.AzureServiceBusProperties; import com.azure.spring.cloud.autoconfigure.servicebus.AzureServiceBusTopicAutoConfiguration; import com.azure.spring.cloud.autoconfigure.servicebus.ServiceBusUtils; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; +import com.azure.spring.cloud.context.core.impl.ServiceBusNamespaceManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusTopicManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusTopicSubscriptionManager; import com.azure.spring.cloud.telemetry.TelemetryCollector; import com.azure.spring.integration.servicebus.topic.ServiceBusTopicOperation; +import com.azure.spring.servicebus.stream.binder.ServiceBusTopicMessageChannelBinder; +import com.azure.spring.servicebus.stream.binder.properties.ServiceBusTopicExtendedBindingProperties; +import com.azure.spring.servicebus.stream.binder.provisioning.ServiceBusChannelProvisioner; +import com.azure.spring.servicebus.stream.binder.provisioning.ServiceBusTopicChannelResourceManagerProvisioner; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -30,15 +32,21 @@ */ @Configuration @ConditionalOnMissingBean(Binder.class) -@Import({AzureServiceBusTopicAutoConfiguration.class, AzureEnvironmentAutoConfiguration.class}) -@EnableConfigurationProperties({AzureServiceBusProperties.class, ServiceBusTopicExtendedBindingProperties.class}) +@Import({ AzureServiceBusTopicAutoConfiguration.class, AzureEnvironmentAutoConfiguration.class }) +@EnableConfigurationProperties({ AzureServiceBusProperties.class, ServiceBusTopicExtendedBindingProperties.class }) public class ServiceBusTopicBinderConfiguration { private static final String SERVICE_BUS_TOPIC_BINDER = "ServiceBusTopicBinder"; private static final String NAMESPACE = "Namespace"; @Autowired(required = false) - private ResourceManagerProvider resourceManagerProvider; + private ServiceBusNamespaceManager serviceBusNamespaceManager; + + @Autowired(required = false) + private ServiceBusTopicManager serviceBusTopicManager; + + @Autowired(required = false) + private ServiceBusTopicSubscriptionManager serviceBusTopicSubscriptionManager; @PostConstruct public void collectTelemetry() { @@ -46,31 +54,36 @@ public void collectTelemetry() { } @Bean - @ConditionalOnBean(ResourceManagerProvider.class) + @ConditionalOnBean({ ServiceBusNamespaceManager.class, ServiceBusTopicManager.class, + ServiceBusTopicSubscriptionManager.class }) @ConditionalOnMissingBean public ServiceBusChannelProvisioner serviceBusChannelProvisioner(AzureServiceBusProperties serviceBusProperties) { - if (this.resourceManagerProvider != null) { - return new ServiceBusTopicChannelResourceManagerProvisioner(resourceManagerProvider, - serviceBusProperties.getNamespace()); + if (this.serviceBusNamespaceManager != null && this.serviceBusTopicManager != null + && this.serviceBusTopicSubscriptionManager != null) { + return new ServiceBusTopicChannelResourceManagerProvisioner(serviceBusNamespaceManager, + serviceBusTopicManager, serviceBusTopicSubscriptionManager, serviceBusProperties.getNamespace()); } else { TelemetryCollector.getInstance().addProperty(SERVICE_BUS_TOPIC_BINDER, NAMESPACE, - ServiceBusUtils.getNamespace(serviceBusProperties.getConnectionString())); + ServiceBusUtils.getNamespace(serviceBusProperties.getConnectionString())); } return new ServiceBusChannelProvisioner(); } @Bean - @ConditionalOnMissingBean({ResourceManagerProvider.class, ServiceBusChannelProvisioner.class}) + @ConditionalOnMissingBean({ ServiceBusNamespaceManager.class, ServiceBusTopicManager.class, + ServiceBusTopicSubscriptionManager.class, ServiceBusChannelProvisioner.class }) public ServiceBusChannelProvisioner serviceBusChannelProvisionerWithResourceManagerProvider() { return new ServiceBusChannelProvisioner(); } @Bean public ServiceBusTopicMessageChannelBinder serviceBusTopicBinder( - ServiceBusChannelProvisioner topicChannelProvisioner, ServiceBusTopicOperation serviceBusTopicOperation, - ServiceBusTopicExtendedBindingProperties bindingProperties) { - ServiceBusTopicMessageChannelBinder binder = - new ServiceBusTopicMessageChannelBinder(null, topicChannelProvisioner, serviceBusTopicOperation); + ServiceBusChannelProvisioner topicChannelProvisioner, + ServiceBusTopicOperation serviceBusTopicOperation, + ServiceBusTopicExtendedBindingProperties bindingProperties) { + + ServiceBusTopicMessageChannelBinder binder = new ServiceBusTopicMessageChannelBinder(null, + topicChannelProvisioner, serviceBusTopicOperation); binder.setBindingProperties(bindingProperties); return binder; } diff --git a/sdk/spring/azure-spring-cloud-stream-binder-servicebus-topic/src/main/java/com/azure/spring/servicebus/stream/binder/provisioning/ServiceBusTopicChannelResourceManagerProvisioner.java b/sdk/spring/azure-spring-cloud-stream-binder-servicebus-topic/src/main/java/com/azure/spring/servicebus/stream/binder/provisioning/ServiceBusTopicChannelResourceManagerProvisioner.java index 65b24fc0912c..12df4f7d8858 100644 --- a/sdk/spring/azure-spring-cloud-stream-binder-servicebus-topic/src/main/java/com/azure/spring/servicebus/stream/binder/provisioning/ServiceBusTopicChannelResourceManagerProvisioner.java +++ b/sdk/spring/azure-spring-cloud-stream-binder-servicebus-topic/src/main/java/com/azure/spring/servicebus/stream/binder/provisioning/ServiceBusTopicChannelResourceManagerProvisioner.java @@ -3,10 +3,12 @@ package com.azure.spring.servicebus.stream.binder.provisioning; +import com.azure.spring.cloud.context.core.impl.ServiceBusNamespaceManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusTopicManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusTopicSubscriptionManager; +import com.azure.spring.cloud.context.core.util.Tuple; import com.microsoft.azure.management.servicebus.ServiceBusNamespace; import com.microsoft.azure.management.servicebus.Topic; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; -import com.azure.spring.cloud.context.core.util.Tuple; import org.springframework.cloud.stream.provisioning.ProvisioningException; import org.springframework.lang.NonNull; import org.springframework.util.Assert; @@ -16,33 +18,37 @@ */ public class ServiceBusTopicChannelResourceManagerProvisioner extends ServiceBusChannelProvisioner { - private final ResourceManagerProvider resourceManagerProvider; + private final ServiceBusNamespaceManager serviceBusNamespaceManager; + private final ServiceBusTopicManager serviceBusTopicManager; + private final ServiceBusTopicSubscriptionManager serviceBusTopicSubscriptionManager; private final String namespace; - public ServiceBusTopicChannelResourceManagerProvisioner(@NonNull ResourceManagerProvider resourceManagerProvider, - @NonNull String namespace) { + public ServiceBusTopicChannelResourceManagerProvisioner( + @NonNull ServiceBusNamespaceManager serviceBusNamespaceManager, + @NonNull ServiceBusTopicManager serviceBusTopicManager, + @NonNull ServiceBusTopicSubscriptionManager serviceBusTopicSubscriptionManager, @NonNull String namespace) { Assert.hasText(namespace, "The namespace can't be null or empty"); - this.resourceManagerProvider = resourceManagerProvider; + this.serviceBusNamespaceManager = serviceBusNamespaceManager; + this.serviceBusTopicManager = serviceBusTopicManager; + this.serviceBusTopicSubscriptionManager = serviceBusTopicSubscriptionManager; this.namespace = namespace; } @Override protected void validateOrCreateForConsumer(String name, String group) { - ServiceBusNamespace namespace = - this.resourceManagerProvider.getServiceBusNamespaceManager().getOrCreate(this.namespace); - Topic topic = this.resourceManagerProvider.getServiceBusTopicManager().getOrCreate(Tuple.of(namespace, name)); + ServiceBusNamespace namespace = serviceBusNamespaceManager.getOrCreate(this.namespace); + Topic topic = serviceBusTopicManager.getOrCreate(Tuple.of(namespace, name)); if (topic == null) { throw new ProvisioningException( String.format("Event hub with name '%s' in namespace '%s' not existed", name, namespace)); } - this.resourceManagerProvider.getServiceBusTopicSubscriptionManager().getOrCreate(Tuple.of(topic, group)); + this.serviceBusTopicSubscriptionManager.getOrCreate(Tuple.of(topic, group)); } @Override protected void validateOrCreateForProducer(String name) { - ServiceBusNamespace namespace = - this.resourceManagerProvider.getServiceBusNamespaceManager().getOrCreate(this.namespace); - this.resourceManagerProvider.getServiceBusTopicManager().getOrCreate(Tuple.of(namespace, name)); + ServiceBusNamespace namespace = serviceBusNamespaceManager.getOrCreate(this.namespace); + serviceBusTopicManager.getOrCreate(Tuple.of(namespace, name)); } } diff --git a/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/AbstractServiceBusSenderFactory.java b/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/AbstractServiceBusSenderFactory.java index dd5e3e59e213..d484d54f43b5 100644 --- a/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/AbstractServiceBusSenderFactory.java +++ b/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/AbstractServiceBusSenderFactory.java @@ -3,7 +3,12 @@ package com.azure.spring.integration.servicebus.factory; -import com.azure.spring.cloud.context.core.api.ResourceManagerProvider; +import com.azure.spring.cloud.context.core.impl.ServiceBusNamespaceManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusQueueManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusTopicManager; +import com.azure.spring.cloud.context.core.impl.ServiceBusTopicSubscriptionManager; + +import javax.annotation.Nullable; /** * Base class of service bus client factory to provide connection string @@ -12,15 +17,37 @@ */ abstract class AbstractServiceBusSenderFactory implements ServiceBusSenderFactory { protected final String connectionString; + + @Nullable + protected ServiceBusNamespaceManager serviceBusNamespaceManager; + @Nullable + protected ServiceBusQueueManager serviceBusQueueManager; + @Nullable + protected ServiceBusTopicManager serviceBusTopicManager; + @Nullable + protected ServiceBusTopicSubscriptionManager serviceBusTopicSubscriptionManager; + @Nullable protected String namespace; - protected ResourceManagerProvider resourceManagerProvider; AbstractServiceBusSenderFactory(String connectionString) { this.connectionString = connectionString; } - public void setResourceManagerProvider(ResourceManagerProvider resourceManagerProvider) { - this.resourceManagerProvider = resourceManagerProvider; + public void setServiceBusNamespaceManager(ServiceBusNamespaceManager serviceBusNamespaceManager) { + this.serviceBusNamespaceManager = serviceBusNamespaceManager; + } + + public void setServiceBusQueueManager(ServiceBusQueueManager serviceBusQueueManager) { + this.serviceBusQueueManager = serviceBusQueueManager; + } + + public void setServiceBusTopicManager(ServiceBusTopicManager serviceBusTopicManager) { + this.serviceBusTopicManager = serviceBusTopicManager; + } + + public void setServiceBusTopicSubscriptionManager( + ServiceBusTopicSubscriptionManager serviceBusTopicSubscriptionManager) { + this.serviceBusTopicSubscriptionManager = serviceBusTopicSubscriptionManager; } public void setNamespace(String namespace) { diff --git a/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/DefaultServiceBusQueueClientFactory.java b/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/DefaultServiceBusQueueClientFactory.java index 15ec0413e831..91dcc59f9cff 100644 --- a/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/DefaultServiceBusQueueClientFactory.java +++ b/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/DefaultServiceBusQueueClientFactory.java @@ -3,6 +3,8 @@ package com.azure.spring.integration.servicebus.factory; +import com.azure.spring.cloud.context.core.util.Memoizer; +import com.azure.spring.cloud.context.core.util.Tuple; import com.azure.spring.integration.servicebus.ServiceBusRuntimeException; import com.microsoft.azure.management.servicebus.ServiceBusNamespace; import com.microsoft.azure.servicebus.IMessageSender; @@ -11,15 +13,12 @@ import com.microsoft.azure.servicebus.ReceiveMode; import com.microsoft.azure.servicebus.primitives.ConnectionStringBuilder; import com.microsoft.azure.servicebus.primitives.ServiceBusException; -import com.azure.spring.cloud.context.core.util.Memoizer; -import com.azure.spring.cloud.context.core.util.Tuple; import org.springframework.util.StringUtils; import java.util.function.Function; /** - * Default implementation of {@link ServiceBusQueueClientFactory}. - * Client will be cached to improve performance + * Default implementation of {@link ServiceBusQueueClientFactory}. Client will be cached to improve performance * * @author Warren Zhu */ @@ -33,10 +32,9 @@ public DefaultServiceBusQueueClientFactory(String connectionString) { } private IQueueClient createQueueClient(String destination) { - if (resourceManagerProvider != null && StringUtils.hasText(namespace)) { - ServiceBusNamespace serviceBusNamespace = - resourceManagerProvider.getServiceBusNamespaceManager().getOrCreate(namespace); - resourceManagerProvider.getServiceBusQueueManager().getOrCreate(Tuple.of(serviceBusNamespace, destination)); + if (StringUtils.hasText(namespace)) { + ServiceBusNamespace serviceBusNamespace = serviceBusNamespaceManager.getOrCreate(namespace); + serviceBusQueueManager.getOrCreate(Tuple.of(serviceBusNamespace, destination)); } try { diff --git a/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/DefaultServiceBusTopicClientFactory.java b/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/DefaultServiceBusTopicClientFactory.java index de6d7e4860a6..b69e22098547 100644 --- a/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/DefaultServiceBusTopicClientFactory.java +++ b/sdk/spring/azure-spring-integration-servicebus/src/main/java/com/azure/spring/integration/servicebus/factory/DefaultServiceBusTopicClientFactory.java @@ -21,46 +21,44 @@ import java.util.function.Function; /** - * Default implementation of {@link ServiceBusTopicClientFactory}. - * Client will be cached to improve performance + * Default implementation of {@link ServiceBusTopicClientFactory}. Client will + * be cached to improve performance * * @author Warren Zhu */ public class DefaultServiceBusTopicClientFactory extends AbstractServiceBusSenderFactory - implements ServiceBusTopicClientFactory { + implements ServiceBusTopicClientFactory { private static final String SUBSCRIPTION_PATH = "%s/subscriptions/%s"; - private final BiFunction subscriptionClientCreator = - Memoizer.memoize(this::createSubscriptionClient); + private final BiFunction subscriptionClientCreator = Memoizer + .memoize(this::createSubscriptionClient); private final Function sendCreator = Memoizer.memoize(this::createTopicClient); + public DefaultServiceBusTopicClientFactory(String connectionString) { super(connectionString); } private ISubscriptionClient createSubscriptionClient(String topicName, String subscription) { - if (resourceManagerProvider != null && StringUtils.hasText(namespace)) { - ServiceBusNamespace serviceBusNamespace = - resourceManagerProvider.getServiceBusNamespaceManager().getOrCreate(namespace); - Topic topic = resourceManagerProvider.getServiceBusTopicManager() - .getOrCreate(Tuple.of(serviceBusNamespace, topicName)); - resourceManagerProvider.getServiceBusTopicSubscriptionManager().getOrCreate(Tuple.of(topic, subscription)); + if (StringUtils.hasText(namespace)) { + ServiceBusNamespace serviceBusNamespace = serviceBusNamespaceManager.getOrCreate(namespace); + Topic topic = serviceBusTopicManager.getOrCreate(Tuple.of(serviceBusNamespace, topicName)); + serviceBusTopicSubscriptionManager.getOrCreate(Tuple.of(topic, subscription)); } String subscriptionPath = String.format(SUBSCRIPTION_PATH, topicName, subscription); try { return new SubscriptionClient(new ConnectionStringBuilder(connectionString, subscriptionPath), - ReceiveMode.PEEKLOCK); + ReceiveMode.PEEKLOCK); } catch (InterruptedException | ServiceBusException e) { throw new ServiceBusRuntimeException("Failed to create service bus subscription client", e); } } private IMessageSender createTopicClient(String topicName) { - if (resourceManagerProvider != null && StringUtils.hasText(namespace)) { - ServiceBusNamespace serviceBusNamespace = - resourceManagerProvider.getServiceBusNamespaceManager().getOrCreate(namespace); - resourceManagerProvider.getServiceBusTopicManager().getOrCreate(Tuple.of(serviceBusNamespace, topicName)); + if (serviceBusNamespaceManager != null && serviceBusTopicManager != null && StringUtils.hasText(namespace)) { + ServiceBusNamespace serviceBusNamespace = serviceBusNamespaceManager.getOrCreate(namespace); + serviceBusTopicManager.getOrCreate(Tuple.of(serviceBusNamespace, topicName)); } try { diff --git a/sdk/spring/azure-spring-starter-storage/src/main/resources/META-INF/spring.provides b/sdk/spring/azure-spring-starter-storage/src/main/resources/META-INF/spring.provides new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sdk/spring/pom.xml b/sdk/spring/pom.xml index f57ab13933bb..0b8484fdfc30 100644 --- a/sdk/spring/pom.xml +++ b/sdk/spring/pom.xml @@ -9,6 +9,7 @@ pom 1.0.0 + azure-identity-spring-library azure-spring-boot azure-spring-boot-starter azure-spring-boot-starter-active-directory