From 2dc356ad6b5f77affb4b4208edf4155825760a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 5 Feb 2026 17:13:03 +0800 Subject: [PATCH 01/22] Add ServiceBus Service Connection for Docker Compose and Testcontainers (#44019) (cherry picked from commit c3591a77a52a6f78b03ac79e5c5a6a66735acbeb) --- eng/versioning/external_dependencies.txt | 2 + sdk/spring/CHANGELOG.md | 26 ++++ .../AzureServiceBusAutoConfiguration.java | 12 +- ...eServiceBusClientBuilderConfiguration.java | 7 +- ...eServiceBusMessagingAutoConfiguration.java | 4 +- .../AzureServiceBusConnectionDetails.java | 12 ++ ...zureServiceBusPropertiesConfiguration.java | 15 +++ ...onfigurationWithConnectionDetailsBean.java | 46 +++++++ ...igurationWithoutConnectionDetailsBean.java | 32 +++++ ...AzureServiceBusAutoConfigurationTests.java | 25 +++- ...iceBusClientBuilderConfigurationTests.java | 9 +- ...iceBusMessagingAutoConfigurationTests.java | 11 +- ...eServiceBusTemplateConfigurationTests.java | 7 +- ...ustomAzureServiceBusConnectionDetails.java | 18 +++ .../spring-cloud-azure-docker-compose/pom.xml | 18 +++ ...DockerComposeConnectionDetailsFactory.java | 48 +++++++ .../main/resources/META-INF/spring.factories | 1 + ...rComposeConnectionDetailsFactoryTests.java | 101 +++++++++++++++ .../service/connection/servicebus/Config.json | 108 ++++++++++++++++ .../servicebus/servicebus-compose.yaml | 30 +++++ .../spring-cloud-azure-testcontainers/pom.xml | 25 ++++ ...eBusContainerConnectionDetailsFactory.java | 34 +++++ .../main/resources/META-INF/spring.factories | 1 + ...ontainerConnectionDetailsFactoryTests.java | 118 ++++++++++++++++++ .../src/test/resources/logback-test.xml | 18 +++ .../src/test/resources/servicebus/Config.json | 108 ++++++++++++++++ 26 files changed, 800 insertions(+), 36 deletions(-) create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusConnectionDetails.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusPropertiesConfiguration.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithConnectionDetailsBean.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithoutConnectionDetailsBean.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/CustomAzureServiceBusConnectionDetails.java create mode 100644 sdk/spring/spring-cloud-azure-docker-compose/src/main/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactory.java create mode 100644 sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactoryTests.java create mode 100644 sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/Config.json create mode 100644 sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/servicebus-compose.yaml create mode 100644 sdk/spring/spring-cloud-azure-testcontainers/src/main/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactory.java create mode 100644 sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java create mode 100644 sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/logback-test.xml create mode 100644 sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/servicebus/Config.json diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index 536a7a7abccc..df323165167f 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -413,6 +413,8 @@ springboot3_org.springframework:spring-web;6.2.9 springboot3_org.springframework:spring-webmvc;6.2.9 springboot3_org.testcontainers:junit-jupiter;1.21.3 springboot3_org.testcontainers:azure;1.21.3 +springboot3_org.awaitility:awaitility;4.3.0 +springboot3_com.microsoft.sqlserver:mssql-jdbc;13.2.1.jre11 # Used for Spring version updates springboot3_org.springframework.boot:spring-boot-dependencies;3.5.4 springboot3_org.springframework.cloud:spring-cloud-dependencies;2025.0.0 diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index 4eb6c594b84d..5635fbb667ad 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -1,5 +1,31 @@ # Release History +## 5.25.0 (Not Released) + +### Spring Cloud Azure Autoconfigure + +This section includes changes in `spring-cloud-azure-autoconfigure` module. + +#### New Features + +- Add ConnectionDetails for ServiceBus. [#44019](https://github.com/Azure/azure-sdk-for-java/pull/44019). + +### Spring Cloud Azure Docker Compose + +This section includes changes in `spring-cloud-azure-docker-compose` module. + +#### New Features + +- Add ServiceBusDockerComposeConnectionDetailsFactory. [#44019](https://github.com/Azure/azure-sdk-for-java/pull/44019). + +### Spring Cloud Azure Test Containers + +This section includes changes in `spring-cloud-azure-testcontainers` module. + +#### New Features + +- Add ServiceBusContainerConnectionDetailsFactory. [#44019](https://github.com/Azure/azure-sdk-for-java/pull/44019). + ## 5.24.1 (2025-12-09) - This release is compatible with Spring Boot 3.5.0-3.5.8, 3.4.0-3.4.12, 3.3.0-3.3.13, 3.2.0-3.2.12, 3.1.0-3.1.12, 3.0.0-3.0.13. (Note: 3.5.x (x>8) and 3.4.y (y>12) should be supported, but they aren't tested with this release.) - This release is compatible with Spring Cloud 2025.0.0, 2024.0.0-2024.0.2, 2023.0.0-2023.0.5, 2022.0.0-2022.0.5. (Note: 2025.0.x(x>0) and 2024.0.y (y>2) should be supported, but they aren't tested with this release.) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfiguration.java index f698130b43c9..05ee2da18780 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfiguration.java @@ -6,12 +6,10 @@ import com.azure.messaging.servicebus.ServiceBusClientBuilder; import com.azure.spring.cloud.autoconfigure.implementation.AzureServiceConfigurationBase; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; -import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusPropertiesConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; /** @@ -22,6 +20,7 @@ @ConditionalOnClass(ServiceBusClientBuilder.class) @ConditionalOnProperty(value = "spring.cloud.azure.servicebus.enabled", havingValue = "true", matchIfMissing = true) @Import({ + AzureServiceBusPropertiesConfiguration.class, AzureServiceBusClientBuilderConfiguration.class, AzureServiceBusProducerClientConfiguration.class, AzureServiceBusConsumerClientConfiguration.class, @@ -29,15 +28,8 @@ }) public class AzureServiceBusAutoConfiguration extends AzureServiceConfigurationBase { - AzureServiceBusAutoConfiguration(AzureGlobalProperties azureGlobalProperties) { super(azureGlobalProperties); } - @Bean - @ConfigurationProperties(AzureServiceBusProperties.PREFIX) - AzureServiceBusProperties azureServiceBusProperties() { - return loadProperties(getAzureGlobalProperties(), new AzureServiceBusProperties()); - } - } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfiguration.java index ebb532cac4dd..9002f082e0c6 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfiguration.java @@ -4,8 +4,8 @@ package com.azure.spring.cloud.autoconfigure.implementation.servicebus; import com.azure.messaging.servicebus.ServiceBusClientBuilder; -import com.azure.spring.cloud.autoconfigure.implementation.condition.ConditionalOnAnyProperty; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusPropertiesConfiguration; import com.azure.spring.cloud.core.customizer.AzureServiceClientBuilderCustomizer; import com.azure.spring.cloud.core.implementation.util.AzureSpringIdentifier; import com.azure.spring.cloud.core.provider.connectionstring.ServiceConnectionStringProvider; @@ -13,15 +13,18 @@ import com.azure.spring.cloud.core.service.AzureServiceType; import com.azure.spring.cloud.service.implementation.servicebus.factory.ServiceBusClientBuilderFactory; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; @Configuration(proxyBeanMethods = false) +@Import(AzureServiceBusPropertiesConfiguration.class) @ConditionalOnClass(ServiceBusClientBuilder.class) -@ConditionalOnAnyProperty(prefix = "spring.cloud.azure.servicebus", name = { "connection-string", "namespace" }) +@ConditionalOnBean(AzureServiceBusProperties.class) class AzureServiceBusClientBuilderConfiguration { private final AzureServiceBusProperties serviceBusProperties; diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfiguration.java index 6a410336a85b..c923c3a5a40e 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfiguration.java @@ -7,8 +7,8 @@ import com.azure.messaging.servicebus.ServiceBusClientBuilder; import com.azure.messaging.servicebus.ServiceBusMessage; import com.azure.messaging.servicebus.ServiceBusReceivedMessage; -import com.azure.spring.cloud.autoconfigure.implementation.condition.ConditionalOnAnyProperty; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusPropertiesConfiguration; import com.azure.spring.cloud.core.customizer.AzureServiceClientBuilderCustomizer; import com.azure.spring.cloud.core.implementation.credential.resolver.AzureTokenCredentialResolver; import com.azure.spring.cloud.core.provider.connectionstring.ServiceConnectionStringProvider; @@ -57,10 +57,10 @@ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(ServiceBusTemplate.class) @ConditionalOnProperty(value = "spring.cloud.azure.servicebus.enabled", havingValue = "true", matchIfMissing = true) -@ConditionalOnAnyProperty(prefix = "spring.cloud.azure.servicebus", name = { "connection-string", "namespace" }) @ConditionalOnBean(AzureServiceBusProperties.class) @AutoConfigureAfter(AzureServiceBusAutoConfiguration.class) @Import({ + AzureServiceBusPropertiesConfiguration.class, AzureServiceBusMessagingAutoConfiguration.ServiceBusTemplateConfiguration.class, AzureServiceBusMessagingAutoConfiguration.ProcessorContainerConfiguration.class }) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusConnectionDetails.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusConnectionDetails.java new file mode 100644 index 000000000000..e9a4dfd3dadb --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusConnectionDetails.java @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties; + +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; + +public interface AzureServiceBusConnectionDetails extends ConnectionDetails { + + String getConnectionString(); + +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusPropertiesConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusPropertiesConfiguration.java new file mode 100644 index 000000000000..9fe7dc8c4860 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusPropertiesConfiguration.java @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Import; + +@Import({ + ConfigurationWithConnectionDetailsBean.class, + ConfigurationWithoutConnectionDetailsBean.class, +}) +@EnableConfigurationProperties +public class AzureServiceBusPropertiesConfiguration { +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithConnectionDetailsBean.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithConnectionDetailsBean.java new file mode 100644 index 000000000000..a52eb3684382 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithConnectionDetailsBean.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties; + +import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.properties.utils.AzureGlobalPropertiesUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.boot.context.properties.bind.BindResult; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; + +@ConditionalOnClass(ConnectionDetails.class) +@ConditionalOnBean(AzureServiceBusConnectionDetails.class) +class ConfigurationWithConnectionDetailsBean { + + private final Environment environment; + private final AzureGlobalProperties globalProperties; + private final AzureServiceBusConnectionDetails connectionDetails; + + ConfigurationWithConnectionDetailsBean( + Environment environment, + AzureGlobalProperties globalProperties, + AzureServiceBusConnectionDetails connectionDetails) { + this.environment = environment; + this.globalProperties = globalProperties; + this.connectionDetails = connectionDetails; + } + + @Bean + AzureServiceBusProperties azureServiceBusProperties() { + AzureServiceBusProperties propertiesLoadFromGlobalProperties = + AzureGlobalPropertiesUtils.loadProperties(globalProperties, new AzureServiceBusProperties()); + BindResult bindResult = Binder.get(environment) + .bind(AzureServiceBusProperties.PREFIX, Bindable.ofInstance(propertiesLoadFromGlobalProperties)); + AzureServiceBusProperties properties = bindResult.isBound() ? bindResult.get() + : propertiesLoadFromGlobalProperties; + properties.setConnectionString(connectionDetails.getConnectionString()); + return properties; + } + +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithoutConnectionDetailsBean.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithoutConnectionDetailsBean.java new file mode 100644 index 000000000000..238dcde601e5 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithoutConnectionDetailsBean.java @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties; + +import com.azure.spring.cloud.autoconfigure.implementation.condition.ConditionalOnAnyProperty; +import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.properties.utils.AzureGlobalPropertiesUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; + +@ConditionalOnMissingBean(type = "com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails") +@ConditionalOnProperty(value = "spring.cloud.azure.servicebus.enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnAnyProperty(prefix = "spring.cloud.azure.servicebus", name = {"connection-string", "namespace"}) +class ConfigurationWithoutConnectionDetailsBean { + + private final AzureGlobalProperties azureGlobalProperties; + + ConfigurationWithoutConnectionDetailsBean(AzureGlobalProperties azureGlobalProperties) { + this.azureGlobalProperties = azureGlobalProperties; + } + + @Bean + @ConditionalOnMissingBean + @ConfigurationProperties(AzureServiceBusProperties.PREFIX) + AzureServiceBusProperties azureServiceBusProperties() { + return AzureGlobalPropertiesUtils.loadProperties(azureGlobalProperties, new AzureServiceBusProperties()); + } + +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfigurationTests.java index 176514dbca37..63195f6cd724 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfigurationTests.java @@ -8,6 +8,7 @@ import com.azure.messaging.servicebus.models.ServiceBusReceiveMode; import com.azure.spring.cloud.autoconfigure.implementation.AbstractAzureServiceConfigurationTests; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; import com.azure.spring.cloud.core.properties.profile.AzureEnvironmentProperties; import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; @@ -81,7 +82,7 @@ void configureAzureServiceBusPropertiesWithGlobalDefaults() { azureProperties.getCredential().setClientSecret("azure-client-secret"); azureProperties.getRetry().getExponential().setBaseDelay(Duration.ofSeconds(2)); - this.contextRunner + this.getMinimalContextRunner() .withBean("azureProperties", AzureGlobalProperties.class, () -> azureProperties) .withPropertyValues( "spring.cloud.azure.servicebus.credential.client-id=servicebus-client-id", @@ -104,7 +105,7 @@ void configureServiceBusDomainNameOverrideGlobalDefault() { AzureGlobalProperties azureProperties = new AzureGlobalProperties(); azureProperties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT); - this.contextRunner + this.getMinimalContextRunner() .withBean("azureProperties", AzureGlobalProperties.class, () -> azureProperties) .withPropertyValues( "spring.cloud.azure.servicebus.domain-name=servicebus.chinacloudapi.cn" @@ -120,7 +121,7 @@ void configureServiceBusDomainNameOverrideGlobalDefault() { @Test void configureAmqpTransportTypeShouldApply() { - this.contextRunner + this.getMinimalContextRunner() .withBean("azureProperties", AzureGlobalProperties.class, AzureGlobalProperties::new) .withPropertyValues("spring.cloud.azure.servicebus.client.transport-type=AmqpWebSockets") .run(context -> { @@ -132,7 +133,7 @@ void configureAmqpTransportTypeShouldApply() { @Test void configureRetryShouldApply() { - this.contextRunner + this.getMinimalContextRunner() .withBean("azureProperties", AzureGlobalProperties.class, AzureGlobalProperties::new) .withPropertyValues( "spring.cloud.azure.servicebus.retry.mode=fixed", @@ -297,4 +298,20 @@ void consumerSubscriptionNameShouldConfigureConsumerClient() { assertThat(context).hasSingleBean(AzureServiceBusConsumerClientConfiguration.class); }); } + + @Test + void connectionDetailsHasHigherPriority() { + String connectionString = String.format(CONNECTION_STRING_FORMAT, "property-namespace"); + this.contextRunner + .withPropertyValues( + "spring.cloud.azure.servicebus.connection-string=" + connectionString + ) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) + .withBean(AzureServiceBusConnectionDetails.class, CustomAzureServiceBusConnectionDetails::new) + .run(context -> { + assertThat(context).hasSingleBean(AzureServiceBusProperties.class); + AzureServiceBusProperties properties = context.getBean(AzureServiceBusProperties.class); + assertEquals(CustomAzureServiceBusConnectionDetails.CONNECTION_STRING, properties.getConnectionString()); + }); + } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfigurationTests.java index 060bf8a1029b..83c1f2d1ed47 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfigurationTests.java @@ -22,7 +22,8 @@ class AzureServiceBusClientBuilderConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(AzureServiceBusClientBuilderConfiguration.class)); + .withConfiguration(AutoConfigurations.of(AzureServiceBusAutoConfiguration.class)) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new); @Test void noConnectionInfoProvidedShouldNotConfigure() { @@ -36,7 +37,6 @@ void connectionStringProvidedShouldConfigure() { .withPropertyValues( "spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace") ) - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .run(context -> { assertThat(context).hasSingleBean(AzureServiceBusClientBuilderConfiguration.class); assertThat(context).hasSingleBean(ServiceBusClientBuilderFactory.class); @@ -55,7 +55,6 @@ void customizerShouldBeCalled() { .withPropertyValues( "spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace") ) - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .withBean("customizer1", ServiceBusBuilderCustomizer.class, () -> customizer) .withBean("customizer2", ServiceBusBuilderCustomizer.class, () -> customizer) .run(context -> assertThat(customizer.getCustomizedTimes()).isEqualTo(2)); @@ -69,7 +68,6 @@ void otherCustomizerShouldNotBeCalled() { .withPropertyValues( "spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace") ) - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .withBean("customizer1", ServiceBusBuilderCustomizer.class, () -> customizer) .withBean("customizer2", ServiceBusBuilderCustomizer.class, () -> customizer) .withBean("customizer3", OtherBuilderCustomizer.class, () -> otherBuilderCustomizer) @@ -81,11 +79,10 @@ void otherCustomizerShouldNotBeCalled() { @Test void configureWithNamespaceAndEmptyConnectionString() { - this.contextRunner.withConfiguration(AutoConfigurations.of(AzureServiceBusAutoConfiguration.class)) + this.contextRunner .withPropertyValues( "spring.cloud.azure.servicebus.connection-string=", "spring.cloud.azure.servicebus.namespace=test-servicebus-namespace") - .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) .run(context -> { assertThat(context).hasSingleBean(AzureServiceBusProperties.class); assertThat(context).doesNotHaveBean(StaticConnectionStringProvider.class); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfigurationTests.java index 0e60e03432c1..7e6991a1a1a3 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfigurationTests.java @@ -6,6 +6,7 @@ import com.azure.core.credential.TokenCredential; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; import com.azure.spring.cloud.core.credential.AzureCredentialResolver; import com.azure.spring.messaging.servicebus.core.ServiceBusProcessorFactory; @@ -31,7 +32,8 @@ class AzureServiceBusMessagingAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(AzureServiceBusMessagingAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(AzureServiceBusAutoConfiguration.class, AzureServiceBusMessagingAutoConfiguration.class)) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new); @Test void disableServiceBusShouldNotConfigure() { @@ -62,7 +64,6 @@ void withoutServiceBusTemplateShouldNotConfigure() { @Test void withoutServiceBusConnectionShouldNotConfigure() { this.contextRunner - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .run(context -> { assertThat(context).doesNotHaveBean(AzureServiceBusMessagingAutoConfiguration.ProcessorContainerConfiguration.class); assertThat(context).doesNotHaveBean(AzureServiceBusMessagingAutoConfiguration.ConsumerContainerConfiguration.class); @@ -75,7 +76,6 @@ void connectionInfoProvidedShouldConfigure() { .withPropertyValues( "spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace") ) - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .run(context -> { assertThat(context).hasSingleBean(ServiceBusProcessorFactory.class); assertThat(context).hasSingleBean(AzureServiceBusMessagingAutoConfiguration.ProcessorContainerConfiguration.class); @@ -91,7 +91,6 @@ void withoutObjectMapperShouldNotConfigure() { "spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace") ) .withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class)) - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .run(context -> assertThatIllegalStateException()); } @@ -99,7 +98,6 @@ void withoutObjectMapperShouldNotConfigure() { void withIsolatedObjectMapper() { this.contextRunner .withPropertyValues("spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace")) - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class)) .run(context -> { assertThat(context).hasBean("defaultServiceBusMessageConverter"); @@ -113,7 +111,6 @@ void withNonIsolatedObjectMapper() { this.contextRunner .withPropertyValues("spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace"), "spring.cloud.azure.message-converter.isolated-object-mapper=false") - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class)) .run(context -> { assertThat(context).hasBean("serviceBusMessageConverter"); @@ -127,7 +124,6 @@ void withUserProvidedObjectMapper() { this.contextRunner .withPropertyValues("spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace"), "spring.cloud.azure.message-converter.isolated-object-mapper=false") - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .withBean("userObjectMapper", ObjectMapper.class, ObjectMapper::new) .withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class)) .run(context -> { @@ -148,7 +144,6 @@ void testCustomTokenCredentialConfiguration() { "spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace"), "spring.cloud.azure.servicebus.credential.token-credential-bean-name=customTokenCredential" ) - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .run(context -> { // Verify that the properties contain the correct credential bean name diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusTemplateConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusTemplateConfigurationTests.java index 075d5a6e873c..709969993a33 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusTemplateConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusTemplateConfigurationTests.java @@ -3,6 +3,7 @@ package com.azure.spring.cloud.autoconfigure.implementation.servicebus; +import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; import com.azure.spring.cloud.service.servicebus.properties.ServiceBusEntityType; import com.azure.spring.messaging.servicebus.core.ServiceBusTemplate; @@ -23,7 +24,8 @@ class AzureServiceBusTemplateConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(AzureServiceBusMessagingAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(AzureServiceBusAutoConfiguration.class, AzureServiceBusMessagingAutoConfiguration.class)) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new); @Test void testAzureServiceBusDisabled() { @@ -55,7 +57,6 @@ void testMessageConverterProvided() { .withPropertyValues( "spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace") ) - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .run(context -> { assertThat(context).hasSingleBean(ServiceBusMessageConverter.class); assertThat(context).hasSingleBean(ServiceBusTemplate.class); @@ -74,7 +75,6 @@ void testMessageEntityTypeProvided() { "spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace"), "spring.cloud.azure.servicebus.entity-type=QUEUE" ) - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .run(context -> { assertServiceBusTemplate(context, ServiceBusEntityType.QUEUE); }); @@ -88,7 +88,6 @@ void testMessageProducerEntityTypeProvided() { "spring.cloud.azure.servicebus.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace"), "spring.cloud.azure.servicebus.producer.entity-type=topic" ) - .withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class) .run(context -> { assertServiceBusTemplate(context, ServiceBusEntityType.TOPIC); }); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/CustomAzureServiceBusConnectionDetails.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/CustomAzureServiceBusConnectionDetails.java new file mode 100644 index 000000000000..8a267548ec62 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/CustomAzureServiceBusConnectionDetails.java @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.servicebus; + +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails; + +import static com.azure.spring.cloud.autoconfigure.implementation.util.TestServiceBusUtils.CONNECTION_STRING_FORMAT; + +public class CustomAzureServiceBusConnectionDetails implements AzureServiceBusConnectionDetails { + + static final String CONNECTION_STRING = String.format(CONNECTION_STRING_FORMAT, "connection-detail-namespace"); + + @Override + public String getConnectionString() { + return CONNECTION_STRING; + } +} diff --git a/sdk/spring/spring-cloud-azure-docker-compose/pom.xml b/sdk/spring/spring-cloud-azure-docker-compose/pom.xml index 3c7ab3ee05ca..e679469f643e 100644 --- a/sdk/spring/spring-cloud-azure-docker-compose/pom.xml +++ b/sdk/spring/spring-cloud-azure-docker-compose/pom.xml @@ -77,6 +77,12 @@ 12.26.1 true + + com.azure + azure-messaging-servicebus + 7.17.17 + true + org.springframework.boot @@ -93,6 +99,12 @@ provided + + com.azure.spring + spring-messaging-azure-servicebus + 7.1.0-beta.1 + test + org.springframework.boot spring-boot-test @@ -117,6 +129,12 @@ 3.27.3 test + + org.awaitility + awaitility + 4.3.0 + test + diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/main/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactory.java b/sdk/spring/spring-cloud-azure-docker-compose/src/main/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactory.java new file mode 100644 index 000000000000..408e03ff0e85 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/main/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactory.java @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.docker.compose.implementation.service.connection.bus; + +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails; +import org.springframework.boot.docker.compose.core.RunningService; +import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; +import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource; + +class ServiceBusDockerComposeConnectionDetailsFactory + extends DockerComposeConnectionDetailsFactory { + + private static final int SERVICE_BUS_PORT = 5672; + + protected ServiceBusDockerComposeConnectionDetailsFactory() { + super("azure-messaging/servicebus-emulator"); + } + + @Override + protected AzureServiceBusConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) { + return new ServiceBusContainerConnectionDetails(source.getRunningService()); + } + + /** + * {@link AzureServiceBusConnectionDetails} backed by a {@code Service Bus} + * {@link RunningService}. + */ + private static class ServiceBusContainerConnectionDetails extends DockerComposeConnectionDetails + implements AzureServiceBusConnectionDetails { + + private final String host; + + private final int port; + + ServiceBusContainerConnectionDetails(RunningService service) { + super(service); + this.host = service.host(); + this.port = service.ports().get(SERVICE_BUS_PORT); + } + + @Override + public String getConnectionString() { + return "Endpoint=sb://%s:%d;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;" + .formatted(this.host, this.port); + } + } +} diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/main/resources/META-INF/spring.factories b/sdk/spring/spring-cloud-azure-docker-compose/src/main/resources/META-INF/spring.factories index 17b7623a3151..6de8408e5b64 100644 --- a/sdk/spring/spring-cloud-azure-docker-compose/src/main/resources/META-INF/spring.factories +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/main/resources/META-INF/spring.factories @@ -1,3 +1,4 @@ org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\ +com.azure.spring.cloud.docker.compose.implementation.service.connection.bus.ServiceBusDockerComposeConnectionDetailsFactory,\ com.azure.spring.cloud.docker.compose.implementation.service.connection.storage.StorageBlobDockerComposeConnectionDetailsFactory,\ com.azure.spring.cloud.docker.compose.implementation.service.connection.storage.StorageQueueDockerComposeConnectionDetailsFactory diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactoryTests.java b/sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactoryTests.java new file mode 100644 index 000000000000..9a14f229e914 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactoryTests.java @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.docker.compose.implementation.service.connection.bus; + +import com.azure.messaging.servicebus.ServiceBusMessage; +import com.azure.messaging.servicebus.ServiceBusSenderClient; +import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusMessagingAutoConfiguration; +import com.azure.spring.cloud.service.servicebus.consumer.ServiceBusErrorHandler; +import com.azure.spring.cloud.service.servicebus.consumer.ServiceBusRecordMessageListener; +import com.azure.spring.messaging.servicebus.core.ServiceBusTemplate; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.support.MessageBuilder; + +import java.time.Duration; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.waitAtMost; + +@SpringBootTest(properties = { + "spring.docker.compose.skip.in-tests=false", + "spring.docker.compose.file=classpath:com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/servicebus-compose.yaml", + "spring.docker.compose.stop.command=down", + "spring.cloud.azure.servicebus.namespace=sbemulatorns", + "spring.cloud.azure.servicebus.entity-name=queue.1", + "spring.cloud.azure.servicebus.entity-type=queue", + "spring.cloud.azure.servicebus.producer.entity-name=queue.1", + "spring.cloud.azure.servicebus.producer.entity-type=queue", + "spring.cloud.azure.servicebus.processor.entity-name=queue.1", + "spring.cloud.azure.servicebus.processor.entity-type=queue" +}) +@EnabledOnOs(OS.LINUX) +class ServiceBusDockerComposeConnectionDetailsFactoryTests { + + @Autowired + private ServiceBusSenderClient senderClient; + + @Autowired + private ServiceBusTemplate serviceBusTemplate; + + @Test + void senderClientCanSendMessage() { + // Wait for Service Bus emulator to be fully ready and queue entity to be available + // The emulator depends on SQL Edge and needs time to initialize the messaging entities + waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -> { + this.senderClient.sendMessage(new ServiceBusMessage("Hello World!")); + }); + + waitAtMost(Duration.ofSeconds(30)).pollDelay(Duration.ofSeconds(5)).untilAsserted(() -> { + assertThat(Config.MESSAGES).contains("Hello World!"); + }); + } + + @Test + void serviceBusTemplateCanSendMessage() { + // Wait for Service Bus emulator to be fully ready and queue entity to be available + // The emulator depends on SQL Edge and needs time to initialize the messaging entities + waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -> { + this.serviceBusTemplate.sendAsync("queue.1", MessageBuilder.withPayload("Hello from ServiceBusTemplate!").build()).block(); + }); + + waitAtMost(Duration.ofSeconds(30)).pollDelay(Duration.ofSeconds(5)).untilAsserted(() -> { + assertThat(Config.MESSAGES).contains("Hello from ServiceBusTemplate!"); + }); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(classes = { + AzureGlobalPropertiesAutoConfiguration.class, + AzureServiceBusAutoConfiguration.class, + AzureServiceBusMessagingAutoConfiguration.class}) + static class Config { + + private static final Set MESSAGES = ConcurrentHashMap.newKeySet(); + + @Bean + ServiceBusRecordMessageListener processMessage() { + return context -> { + MESSAGES.add(context.getMessage().getBody().toString()); + }; + } + + @Bean + ServiceBusErrorHandler errorHandler() { + // No-op error handler for tests: acknowledge errors without affecting test execution. + return (context) -> { + }; + } + } +} diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/Config.json b/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/Config.json new file mode 100644 index 000000000000..256f4fbc40c4 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/Config.json @@ -0,0 +1,108 @@ +{ + "UserConfig": { + "Namespaces": [ + { + "Name": "sbemulatorns", + "Queues": [ + { + "Name": "queue.1", + "Properties": { + "DeadLetteringOnMessageExpiration": false, + "DefaultMessageTimeToLive": "PT1H", + "DuplicateDetectionHistoryTimeWindow": "PT20S", + "ForwardDeadLetteredMessagesTo": "", + "ForwardTo": "", + "LockDuration": "PT1M", + "MaxDeliveryCount": 10, + "RequiresDuplicateDetection": false, + "RequiresSession": false + } + } + ], + + "Topics": [ + { + "Name": "topic.1", + "Properties": { + "DefaultMessageTimeToLive": "PT1H", + "DuplicateDetectionHistoryTimeWindow": "PT20S", + "RequiresDuplicateDetection": false + }, + "Subscriptions": [ + { + "Name": "subscription.1", + "Properties": { + "DeadLetteringOnMessageExpiration": false, + "DefaultMessageTimeToLive": "PT1H", + "LockDuration": "PT1M", + "MaxDeliveryCount": 10, + "ForwardDeadLetteredMessagesTo": "", + "ForwardTo": "", + "RequiresSession": false + }, + "Rules": [ + { + "Name": "app-prop-filter-1", + "Properties": { + "FilterType": "Correlation", + "CorrelationFilter": { + "ContentType": "application/text", + "CorrelationId": "id1", + "Label": "subject1", + "MessageId": "msgid1", + "ReplyTo": "someQueue", + "ReplyToSessionId": "sessionId", + "SessionId": "session1", + "To": "xyz" + } + } + } + ] + }, + { + "Name": "subscription.2", + "Properties": { + "DeadLetteringOnMessageExpiration": false, + "DefaultMessageTimeToLive": "PT1H", + "LockDuration": "PT1M", + "MaxDeliveryCount": 10, + "ForwardDeadLetteredMessagesTo": "", + "ForwardTo": "", + "RequiresSession": false + }, + "Rules": [ + { + "Name": "user-prop-filter-1", + "Properties": { + "FilterType": "Correlation", + "CorrelationFilter": { + "Properties": { + "prop3": "value3" + } + } + } + } + ] + }, + { + "Name": "subscription.3", + "Properties": { + "DeadLetteringOnMessageExpiration": false, + "DefaultMessageTimeToLive": "PT1H", + "LockDuration": "PT1M", + "MaxDeliveryCount": 10, + "ForwardDeadLetteredMessagesTo": "", + "ForwardTo": "", + "RequiresSession": false + } + } + ] + } + ] + } + ], + "Logging": { + "Type": "File" + } + } +} diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/servicebus-compose.yaml b/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/servicebus-compose.yaml new file mode 100644 index 000000000000..87bfa50479b0 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/servicebus-compose.yaml @@ -0,0 +1,30 @@ +services: + servicebus: + image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest + pull_policy: always + volumes: + - "./Config.json:/ServiceBus_Emulator/ConfigFiles/Config.json" + ports: + - "5672" + environment: + SQL_SERVER: sqledge + MSSQL_SA_PASSWORD: A_Str0ng_Required_Password + ACCEPT_EULA: Y + depends_on: + - sqledge + networks: + sb-emulator: + aliases: + - "sb-emulator" + sqledge: + image: "mcr.microsoft.com/azure-sql-edge:latest" + networks: + sb-emulator: + aliases: + - "sqledge" + environment: + ACCEPT_EULA: Y + MSSQL_SA_PASSWORD: A_Str0ng_Required_Password + +networks: + sb-emulator: diff --git a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml index a5cce841390c..9beab539ced0 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml +++ b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml @@ -78,6 +78,18 @@ 12.26.1 true + + com.azure + azure-messaging-servicebus + 7.17.13 + true + + + com.azure.spring + spring-messaging-azure-servicebus + 5.24.1 + test + org.springframework.boot @@ -90,6 +102,12 @@ 1.21.3 true + + com.microsoft.sqlserver + mssql-jdbc + 13.2.1.jre11 + true + @@ -124,6 +142,12 @@ 1.21.3 test + + org.awaitility + awaitility + 4.3.0 + test + @@ -169,6 +193,7 @@ org.springframework.boot:spring-boot-testcontainers:[3.5.4] org.testcontainers:azure:[1.21.3] + com.microsoft.sqlserver:mssql-jdbc:[13.2.1.jre11] diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/main/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactory.java b/sdk/spring/spring-cloud-azure-testcontainers/src/main/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactory.java new file mode 100644 index 000000000000..d378bcf5fa64 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/main/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactory.java @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.testcontainers.implementation.service.connection.bus; + +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; +import org.testcontainers.azure.ServiceBusEmulatorContainer; + +class ServiceBusContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory { + + @Override + protected AzureServiceBusConnectionDetails getContainerConnectionDetails(ContainerConnectionSource source) { + return new ServiceBusContainerConnectionDetails(source); + } + + /** + * {@link AzureServiceBusConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static class ServiceBusContainerConnectionDetails extends ContainerConnectionDetails + implements AzureServiceBusConnectionDetails { + + ServiceBusContainerConnectionDetails(ContainerConnectionSource source) { + super(source); + } + + @Override + public String getConnectionString() { + return getContainer().getConnectionString(); + } + } +} diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/main/resources/META-INF/spring.factories b/sdk/spring/spring-cloud-azure-testcontainers/src/main/resources/META-INF/spring.factories index 6543d8914a45..8d3008a6217a 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/src/main/resources/META-INF/spring.factories +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\ com.azure.spring.cloud.testcontainers.implementation.service.connection.cosmos.CosmosContainerConnectionDetailsFactory,\ +com.azure.spring.cloud.testcontainers.implementation.service.connection.bus.ServiceBusContainerConnectionDetailsFactory,\ com.azure.spring.cloud.testcontainers.implementation.service.connection.storage.StorageBlobContainerConnectionDetailsFactory,\ com.azure.spring.cloud.testcontainers.implementation.service.connection.storage.StorageQueueContainerConnectionDetailsFactory diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java b/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java new file mode 100644 index 000000000000..0771ab5da8a4 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.testcontainers.implementation.service.connection.bus; + +import com.azure.messaging.servicebus.ServiceBusMessage; +import com.azure.messaging.servicebus.ServiceBusSenderClient; +import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusMessagingAutoConfiguration; +import com.azure.spring.cloud.service.servicebus.consumer.ServiceBusErrorHandler; +import com.azure.spring.cloud.service.servicebus.consumer.ServiceBusRecordMessageListener; +import com.azure.spring.messaging.servicebus.core.ServiceBusTemplate; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import org.testcontainers.azure.ServiceBusEmulatorContainer; +import org.testcontainers.containers.MSSQLServerContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.MountableFile; + +import java.time.Duration; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.waitAtMost; + +@SpringJUnitConfig +@TestPropertySource(properties = { "spring.cloud.azure.servicebus.entity-name=queue.1", + "spring.cloud.azure.servicebus.entity-type=queue" }) +@Testcontainers +@EnabledOnOs(OS.LINUX) +class ServiceBusContainerConnectionDetailsFactoryTests { + + private static final Network NETWORK = Network.newNetwork(); + + private static final MSSQLServerContainer SQLSERVER = new MSSQLServerContainer<>( + "mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04") + .acceptLicense() + .withNetwork(NETWORK) + .withNetworkAliases("sqlserver"); + + @Container + @ServiceConnection + private static final ServiceBusEmulatorContainer SERVICE_BUS = new ServiceBusEmulatorContainer( + "mcr.microsoft.com/azure-messaging/servicebus-emulator:latest") + .acceptLicense() + .withCopyFileToContainer(MountableFile.forClasspathResource("servicebus/Config.json"), + "/ServiceBus_Emulator/ConfigFiles/Config.json") + .withNetwork(NETWORK) + .withMsSqlServerContainer(SQLSERVER); + + @Autowired + private ServiceBusSenderClient senderClient; + + @Autowired + private ServiceBusTemplate serviceBusTemplate; + + @Test + void senderClientCanSendMessage() { + // Wait for Service Bus emulator to be fully ready and queue entity to be available + waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -> { + this.senderClient.sendMessage(new ServiceBusMessage("Hello World!")); + }); + + waitAtMost(Duration.ofSeconds(30)).untilAsserted(() -> { + assertThat(Config.MESSAGES).contains("Hello World!"); + }); + } + + @Test + void serviceBusTemplateCanSendMessage() { + // Wait for Service Bus emulator to be fully ready and queue entity to be available + waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -> { + this.serviceBusTemplate.sendAsync("queue.1", MessageBuilder.withPayload("Hello from ServiceBusTemplate!").build()).block(); + }); + + waitAtMost(Duration.ofSeconds(30)).untilAsserted(() -> { + assertThat(Config.MESSAGES).contains("Hello from ServiceBusTemplate!"); + }); + } + + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(classes = {AzureGlobalPropertiesAutoConfiguration.class, + AzureServiceBusAutoConfiguration.class, + AzureServiceBusMessagingAutoConfiguration.class}) + static class Config { + + private static final Set MESSAGES = ConcurrentHashMap.newKeySet(); + + @Bean + ServiceBusRecordMessageListener processMessage() { + return context -> { + MESSAGES.add(context.getMessage().getBody().toString()); + }; + } + + @Bean + ServiceBusErrorHandler errorHandler() { + // No-op error handler for tests: acknowledge errors without affecting test execution. + return (context) -> { + }; + } + + } +} diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/logback-test.xml b/sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/logback-test.xml new file mode 100644 index 000000000000..f760dc6205b3 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/servicebus/Config.json b/sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/servicebus/Config.json new file mode 100644 index 000000000000..256f4fbc40c4 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/servicebus/Config.json @@ -0,0 +1,108 @@ +{ + "UserConfig": { + "Namespaces": [ + { + "Name": "sbemulatorns", + "Queues": [ + { + "Name": "queue.1", + "Properties": { + "DeadLetteringOnMessageExpiration": false, + "DefaultMessageTimeToLive": "PT1H", + "DuplicateDetectionHistoryTimeWindow": "PT20S", + "ForwardDeadLetteredMessagesTo": "", + "ForwardTo": "", + "LockDuration": "PT1M", + "MaxDeliveryCount": 10, + "RequiresDuplicateDetection": false, + "RequiresSession": false + } + } + ], + + "Topics": [ + { + "Name": "topic.1", + "Properties": { + "DefaultMessageTimeToLive": "PT1H", + "DuplicateDetectionHistoryTimeWindow": "PT20S", + "RequiresDuplicateDetection": false + }, + "Subscriptions": [ + { + "Name": "subscription.1", + "Properties": { + "DeadLetteringOnMessageExpiration": false, + "DefaultMessageTimeToLive": "PT1H", + "LockDuration": "PT1M", + "MaxDeliveryCount": 10, + "ForwardDeadLetteredMessagesTo": "", + "ForwardTo": "", + "RequiresSession": false + }, + "Rules": [ + { + "Name": "app-prop-filter-1", + "Properties": { + "FilterType": "Correlation", + "CorrelationFilter": { + "ContentType": "application/text", + "CorrelationId": "id1", + "Label": "subject1", + "MessageId": "msgid1", + "ReplyTo": "someQueue", + "ReplyToSessionId": "sessionId", + "SessionId": "session1", + "To": "xyz" + } + } + } + ] + }, + { + "Name": "subscription.2", + "Properties": { + "DeadLetteringOnMessageExpiration": false, + "DefaultMessageTimeToLive": "PT1H", + "LockDuration": "PT1M", + "MaxDeliveryCount": 10, + "ForwardDeadLetteredMessagesTo": "", + "ForwardTo": "", + "RequiresSession": false + }, + "Rules": [ + { + "Name": "user-prop-filter-1", + "Properties": { + "FilterType": "Correlation", + "CorrelationFilter": { + "Properties": { + "prop3": "value3" + } + } + } + } + ] + }, + { + "Name": "subscription.3", + "Properties": { + "DeadLetteringOnMessageExpiration": false, + "DefaultMessageTimeToLive": "PT1H", + "LockDuration": "PT1M", + "MaxDeliveryCount": 10, + "ForwardDeadLetteredMessagesTo": "", + "ForwardTo": "", + "RequiresSession": false + } + } + ] + } + ] + } + ], + "Logging": { + "Type": "File" + } + } +} From 92ec2145c7cab0ba6b16be31b5701169258a6b35 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 09:27:46 +0800 Subject: [PATCH 02/22] Add ConnectionDetails support for EventHubs (#47926) (cherry picked from commit ba63ffc3468364cfb8b93fd7b374584afad1993d) --- .../cosmos/AzureCosmosAutoConfiguration.java | 8 +- ...AzureBlobCheckpointStoreConfiguration.java | 2 + .../AzureEventHubsAutoConfiguration.java | 35 +------- ...reEventHubsClientBuilderConfiguration.java | 37 +++++++- ...eEventHubsConsumerClientConfiguration.java | 2 +- ...reEventHubsMessagingAutoConfiguration.java | 2 - ...EventHubsProcessorClientConfiguration.java | 9 +- ...eEventHubsProducerClientConfiguration.java | 2 +- .../AzureEventHubsKafkaAutoConfiguration.java | 18 ++++ .../AzureEventHubsConnectionDetails.java | 12 +++ ...AzureEventHubsPropertiesConfiguration.java | 15 ++++ ...onfigurationWithConnectionDetailsBean.java | 46 ++++++++++ ...igurationWithoutConnectionDetailsBean.java | 32 +++++++ .../AzureServiceBusAutoConfiguration.java | 8 +- ...eServiceBusClientBuilderConfiguration.java | 11 +++ .../AzureEventHubsAutoConfigurationTests.java | 18 +++- ...ntHubsClientBuilderConfigurationTests.java | 66 ++++++++++++-- ...tHubsConsumerClientConfigurationTests.java | 2 + ...ntHubsMessagingAutoConfigurationTests.java | 1 - ...tHubsProducerClientConfigurationTests.java | 3 + ...CustomAzureEventHubsConnectionDetails.java | 16 ++++ ...eEventHubsKafkaAutoConfigurationTests.java | 34 +++++++- ...iceBusClientBuilderConfigurationTests.java | 30 ++++++- .../spring-cloud-azure-docker-compose/pom.xml | 18 ++-- ...DockerComposeConnectionDetailsFactory.java | 48 +++++++++++ .../main/resources/META-INF/spring.factories | 3 +- ...rComposeConnectionDetailsFactoryTests.java | 15 +++- ...rComposeConnectionDetailsFactoryTests.java | 65 ++++++++++++++ .../{servicebus => bus}/Config.json | 0 .../servicebus-compose.yaml | 0 .../service/connection/hubs/Config.json | 20 +++++ .../connection/hubs/eventhubs-compose.yaml | 34 ++++++++ .../spring-cloud-azure-testcontainers/pom.xml | 30 ++++--- ...HubsContainerConnectionDetailsFactory.java | 34 ++++++++ .../main/resources/META-INF/spring.factories | 3 +- ...ontainerConnectionDetailsFactoryTests.java | 12 +++ ...ontainerConnectionDetailsFactoryTests.java | 86 +++++++++++++++++++ .../src/test/resources/eventhubs/Config.json | 20 +++++ 38 files changed, 704 insertions(+), 93 deletions(-) create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/AzureEventHubsConnectionDetails.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/AzureEventHubsPropertiesConfiguration.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithConnectionDetailsBean.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithoutConnectionDetailsBean.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/CustomAzureEventHubsConnectionDetails.java create mode 100644 sdk/spring/spring-cloud-azure-docker-compose/src/main/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/EventHubsDockerComposeConnectionDetailsFactory.java create mode 100644 sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/EventHubsDockerComposeConnectionDetailsFactoryTests.java rename sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/{servicebus => bus}/Config.json (100%) rename sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/{servicebus => bus}/servicebus-compose.yaml (100%) create mode 100644 sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/Config.json create mode 100644 sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/eventhubs-compose.yaml create mode 100644 sdk/spring/spring-cloud-azure-testcontainers/src/main/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/hubs/EventHubsContainerConnectionDetailsFactory.java create mode 100644 sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/hubs/EventHubsContainerConnectionDetailsFactoryTests.java create mode 100644 sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/eventhubs/Config.json diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/cosmos/AzureCosmosAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/cosmos/AzureCosmosAutoConfiguration.java index c5b09a30c5f7..2b725965744d 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/cosmos/AzureCosmosAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/cosmos/AzureCosmosAutoConfiguration.java @@ -4,8 +4,6 @@ package com.azure.spring.cloud.autoconfigure.implementation.cosmos; import com.azure.cosmos.CosmosClientBuilder; -import com.azure.spring.cloud.autoconfigure.implementation.AzureServiceConfigurationBase; -import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.cosmos.properties.AzureCosmosPropertiesConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -21,9 +19,5 @@ CosmosClientConfiguration.class }) @ConditionalOnClass(CosmosClientBuilder.class) -public class AzureCosmosAutoConfiguration extends AzureServiceConfigurationBase { - - protected AzureCosmosAutoConfiguration(AzureGlobalProperties azureProperties) { - super(azureProperties); - } +public class AzureCosmosAutoConfiguration { } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureBlobCheckpointStoreConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureBlobCheckpointStoreConfiguration.java index 16fd2746bb4d..48ce309d0e8d 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureBlobCheckpointStoreConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureBlobCheckpointStoreConfiguration.java @@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Qualifier; +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; @@ -32,6 +33,7 @@ */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ BlobCheckpointStore.class, EventHubClientBuilder.class}) +@ConditionalOnBean(AzureEventHubsProperties.class) @ConditionalOnProperty(prefix = "spring.cloud.azure.eventhubs.processor.checkpoint-store", name = { "container-name", "account-name" }) class AzureBlobCheckpointStoreConfiguration { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfiguration.java index 13aef3712767..20bef7bf3721 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfiguration.java @@ -4,20 +4,10 @@ package com.azure.spring.cloud.autoconfigure.implementation.eventhubs; import com.azure.messaging.eventhubs.EventHubClientBuilder; -import com.azure.spring.cloud.autoconfigure.implementation.AzureServiceConfigurationBase; -import com.azure.spring.cloud.autoconfigure.implementation.condition.ConditionalOnAnyProperty; -import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; -import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsProperties; -import com.azure.spring.cloud.core.provider.connectionstring.ServiceConnectionStringProvider; -import com.azure.spring.cloud.core.provider.connectionstring.StaticConnectionStringProvider; -import com.azure.spring.cloud.core.service.AzureServiceType; +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsPropertiesConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 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.ConfigurationProperties; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; /** @@ -27,33 +17,14 @@ */ @ConditionalOnClass(EventHubClientBuilder.class) @ConditionalOnProperty(value = "spring.cloud.azure.eventhubs.enabled", havingValue = "true", matchIfMissing = true) -@ConditionalOnAnyProperty(prefix = "spring.cloud.azure.eventhubs", name = { "connection-string", "namespace" }) @Import({ + AzureEventHubsPropertiesConfiguration.class, AzureEventHubsClientBuilderConfiguration.class, AzureEventHubsConsumerClientConfiguration.class, AzureEventHubsProducerClientConfiguration.class, AzureBlobCheckpointStoreConfiguration.class, AzureEventHubsProcessorClientConfiguration.class }) -public class AzureEventHubsAutoConfiguration extends AzureServiceConfigurationBase { - - AzureEventHubsAutoConfiguration(AzureGlobalProperties azureGlobalProperties) { - super(azureGlobalProperties); - } - - @Bean - @ConfigurationProperties(AzureEventHubsProperties.PREFIX) - AzureEventHubsProperties azureEventHubsProperties() { - return loadProperties(getAzureGlobalProperties(), new AzureEventHubsProperties()); - } - - @Bean - @ConditionalOnExpression("'${spring.cloud.azure.eventhubs.connection-string:}' != ''") - @ConditionalOnMissingBean(value = AzureServiceType.EventHubs.class, parameterizedContainer = ServiceConnectionStringProvider.class) - StaticConnectionStringProvider eventHubsStaticConnectionStringProvider( - AzureEventHubsProperties eventHubsProperties) { - return new StaticConnectionStringProvider<>(AzureServiceType.EVENT_HUBS, - eventHubsProperties.getConnectionString()); - } +public class AzureEventHubsAutoConfiguration { } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsClientBuilderConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsClientBuilderConfiguration.java index 9f4471b9fe0c..43f98426b7ea 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsClientBuilderConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsClientBuilderConfiguration.java @@ -4,18 +4,23 @@ package com.azure.spring.cloud.autoconfigure.implementation.eventhubs; import com.azure.messaging.eventhubs.EventHubClientBuilder; -import com.azure.spring.cloud.autoconfigure.implementation.condition.ConditionalOnAnyProperty; +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails; import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsProperties; +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsPropertiesConfiguration; import com.azure.spring.cloud.core.customizer.AzureServiceClientBuilderCustomizer; import com.azure.spring.cloud.core.implementation.util.AzureSpringIdentifier; import com.azure.spring.cloud.core.provider.connectionstring.ServiceConnectionStringProvider; +import com.azure.spring.cloud.core.provider.connectionstring.StaticConnectionStringProvider; import com.azure.spring.cloud.core.service.AzureServiceType; import com.azure.spring.cloud.service.implementation.eventhubs.factory.EventHubClientBuilderFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import static com.azure.spring.cloud.autoconfigure.implementation.context.AzureContextUtils.EVENT_HUB_CLIENT_BUILDER_FACTORY_BEAN_NAME; @@ -24,9 +29,16 @@ * */ @Configuration(proxyBeanMethods = false) -@ConditionalOnAnyProperty(prefix = "spring.cloud.azure.eventhubs", name = { "connection-string", "namespace" }) +@Import(AzureEventHubsPropertiesConfiguration.class) +@ConditionalOnBean(AzureEventHubsProperties.class) class AzureEventHubsClientBuilderConfiguration { + private final AzureEventHubsProperties eventHubsProperties; + + AzureEventHubsClientBuilderConfiguration(AzureEventHubsProperties eventHubsProperties) { + this.eventHubsProperties = eventHubsProperties; + } + @Bean @ConditionalOnMissingBean EventHubClientBuilder eventHubClientBuilder(@Qualifier(EVENT_HUB_CLIENT_BUILDER_FACTORY_BEAN_NAME) @@ -36,10 +48,10 @@ EventHubClientBuilder eventHubClientBuilder(@Qualifier(EVENT_HUB_CLIENT_BUILDER_ @Bean(EVENT_HUB_CLIENT_BUILDER_FACTORY_BEAN_NAME) @ConditionalOnMissingBean - EventHubClientBuilderFactory eventHubClientBuilderFactory(AzureEventHubsProperties properties, + EventHubClientBuilderFactory eventHubClientBuilderFactory( ObjectProvider> connectionStringProviders, ObjectProvider> customizers) { - final EventHubClientBuilderFactory factory = new EventHubClientBuilderFactory(properties); + final EventHubClientBuilderFactory factory = new EventHubClientBuilderFactory(this.eventHubsProperties); factory.setSpringIdentifier(AzureSpringIdentifier.AZURE_SPRING_EVENT_HUBS); connectionStringProviders.orderedStream().findFirst().ifPresent(factory::setConnectionStringProvider); @@ -47,4 +59,21 @@ EventHubClientBuilderFactory eventHubClientBuilderFactory(AzureEventHubsProperti return factory; } + @Bean + @ConditionalOnExpression("'${spring.cloud.azure.eventhubs.connection-string:}' != ''") + @ConditionalOnMissingBean(value = AzureServiceType.EventHubs.class, parameterizedContainer = ServiceConnectionStringProvider.class) + StaticConnectionStringProvider eventHubsStaticConnectionStringProvider() { + return new StaticConnectionStringProvider<>(AzureServiceType.EVENT_HUBS, + this.eventHubsProperties.getConnectionString()); + } + + @Bean + @ConditionalOnBean(AzureEventHubsConnectionDetails.class) + @ConditionalOnMissingBean(value = AzureServiceType.EventHubs.class, parameterizedContainer = ServiceConnectionStringProvider.class) + StaticConnectionStringProvider eventHubsConnectionDetailsStaticConnectionStringProvider( + AzureEventHubsConnectionDetails connectionDetails) { + return new StaticConnectionStringProvider<>(AzureServiceType.EVENT_HUBS, + connectionDetails.getConnectionString()); + } + } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsConsumerClientConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsConsumerClientConfiguration.java index f1a8bc145d36..dc0fc7d1ae07 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsConsumerClientConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsConsumerClientConfiguration.java @@ -38,11 +38,11 @@ AzureEventHubsConsumerClientConfiguration.SharedConsumerConnectionConfiguration.class }) @ConditionalOnAnyProperty(prefix = "spring.cloud.azure.eventhubs", name = { "event-hub-name", "consumer.event-hub-name" }) +@ConditionalOnBean(AzureEventHubsProperties.class) @ConditionalOnProperty(prefix = "spring.cloud.azure.eventhubs.consumer", name = "consumer-group") class AzureEventHubsConsumerClientConfiguration { @ConditionalOnMissingProperty(prefix = "spring.cloud.azure.eventhubs.consumer", name = { "connection-string", "namespace" }) - @ConditionalOnAnyProperty(prefix = "spring.cloud.azure.eventhubs", name = { "connection-string", "namespace" }) @ConditionalOnBean(EventHubClientBuilder.class) @Configuration(proxyBeanMethods = false) static class SharedConsumerConnectionConfiguration { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsMessagingAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsMessagingAutoConfiguration.java index 5526f7e995a8..d2cf8f30348b 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsMessagingAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsMessagingAutoConfiguration.java @@ -6,7 +6,6 @@ import com.azure.core.credential.TokenCredential; import com.azure.messaging.eventhubs.CheckpointStore; import com.azure.messaging.eventhubs.EventData; -import com.azure.spring.cloud.autoconfigure.implementation.condition.ConditionalOnAnyProperty; import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsProperties; import com.azure.spring.cloud.core.implementation.credential.resolver.AzureTokenCredentialResolver; import com.azure.spring.cloud.core.provider.connectionstring.ServiceConnectionStringProvider; @@ -52,7 +51,6 @@ @ConditionalOnClass(EventHubsTemplate.class) @AutoConfigureAfter(AzureEventHubsAutoConfiguration.class) @ConditionalOnProperty(value = "spring.cloud.azure.eventhubs.enabled", havingValue = "true", matchIfMissing = true) -@ConditionalOnAnyProperty(prefix = "spring.cloud.azure.eventhubs", name = {"connection-string", "namespace"}) @ConditionalOnBean(AzureEventHubsProperties.class) @Import({ AzureEventHubsMessagingAutoConfiguration.EventHubsTemplateConfiguration.class, diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProcessorClientConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProcessorClientConfiguration.java index 75464f665054..3ec7c9426572 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProcessorClientConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProcessorClientConfiguration.java @@ -33,7 +33,7 @@ */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(EventProcessorClientBuilder.class) -@ConditionalOnBean({ MessageListener.class, CheckpointStore.class, EventHubsErrorHandler.class }) +@ConditionalOnBean({ MessageListener.class, CheckpointStore.class, EventHubsErrorHandler.class, AzureEventHubsProperties.class }) @Conditional(AzureEventHubsProcessorClientConfiguration.ProcessorAvailableCondition.class) class AzureEventHubsProcessorClientConfiguration { @@ -119,12 +119,5 @@ static class ConsumerGroup { ConsumerGroup() { } } - - @ConditionalOnAnyProperty( - prefix = "spring.cloud.azure.eventhubs", - name = { "namespace", "connection-string", "processor.namespace", "processor.connection-string" }) - static class ConnectionInfo { - - } } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProducerClientConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProducerClientConfiguration.java index 25a6bca7d4c8..5e5cae605531 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProducerClientConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProducerClientConfiguration.java @@ -30,11 +30,11 @@ * */ @Configuration(proxyBeanMethods = false) +@ConditionalOnBean(AzureEventHubsProperties.class) @ConditionalOnAnyProperty(prefix = "spring.cloud.azure.eventhubs", name = { "event-hub-name", "producer.event-hub-name" }) class AzureEventHubsProducerClientConfiguration { @ConditionalOnMissingProperty(prefix = "spring.cloud.azure.eventhubs.producer", name = { "connection-string", "namespace" }) - @ConditionalOnAnyProperty(prefix = "spring.cloud.azure.eventhubs", name = { "connection-string", "namespace" }) @ConditionalOnBean(EventHubClientBuilder.class) @Configuration(proxyBeanMethods = false) static class SharedProducerConnectionConfiguration { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaAutoConfiguration.java index ed73cf8d4337..d018ef936afa 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaAutoConfiguration.java @@ -4,6 +4,7 @@ package com.azure.spring.cloud.autoconfigure.implementation.eventhubs.kafka; import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.AzureEventHubsAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails; import com.azure.spring.cloud.autoconfigure.implementation.kafka.AzureEventHubsKafkaOAuth2AutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.resourcemanager.AzureEventHubsResourceManagerAutoConfiguration; import com.azure.spring.cloud.core.implementation.connectionstring.EventHubsConnectionString; @@ -56,6 +57,23 @@ StaticConnectionStringProvider eventHubsKafkaConnect return new StaticConnectionStringProvider<>(AzureServiceType.EVENT_HUBS, connectionString); } + @Bean + @ConditionalOnBean(AzureEventHubsConnectionDetails.class) + @ConditionalOnMissingBean(value = AzureServiceType.EventHubs.class, parameterizedContainer = ServiceConnectionStringProvider.class) + StaticConnectionStringProvider eventHubsKafkaConnectionDetailsConnectionString( + AzureEventHubsConnectionDetails connectionDetails) { + + String connectionString = connectionDetails.getConnectionString(); + try { + new EventHubsConnectionString(connectionString); + } catch (Exception e) { + LOGGER.error("A valid Event Hubs connection string must be provided"); + throw e; + } + + return new StaticConnectionStringProvider<>(AzureServiceType.EVENT_HUBS, connectionString); + } + @Bean @ConditionalOnBean(value = AzureServiceType.EventHubs.class, parameterizedContainer = ServiceConnectionStringProvider.class) static KafkaPropertiesBeanPostProcessor kafkaPropertiesBeanPostProcessor() { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/AzureEventHubsConnectionDetails.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/AzureEventHubsConnectionDetails.java new file mode 100644 index 000000000000..9d68aa05b60e --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/AzureEventHubsConnectionDetails.java @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties; + +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; + +public interface AzureEventHubsConnectionDetails extends ConnectionDetails { + + String getConnectionString(); + +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/AzureEventHubsPropertiesConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/AzureEventHubsPropertiesConfiguration.java new file mode 100644 index 000000000000..b44c3ba37eb2 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/AzureEventHubsPropertiesConfiguration.java @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Import; + +@Import({ + ConfigurationWithConnectionDetailsBean.class, + ConfigurationWithoutConnectionDetailsBean.class, +}) +@EnableConfigurationProperties +public class AzureEventHubsPropertiesConfiguration { +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithConnectionDetailsBean.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithConnectionDetailsBean.java new file mode 100644 index 000000000000..7eeea4a62f26 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithConnectionDetailsBean.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties; + +import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.properties.utils.AzureGlobalPropertiesUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.boot.context.properties.bind.BindResult; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; + +@ConditionalOnClass(ConnectionDetails.class) +@ConditionalOnBean(AzureEventHubsConnectionDetails.class) +class ConfigurationWithConnectionDetailsBean { + + private final Environment environment; + private final AzureGlobalProperties globalProperties; + private final AzureEventHubsConnectionDetails connectionDetails; + + ConfigurationWithConnectionDetailsBean( + Environment environment, + AzureGlobalProperties globalProperties, + AzureEventHubsConnectionDetails connectionDetails) { + this.environment = environment; + this.globalProperties = globalProperties; + this.connectionDetails = connectionDetails; + } + + @Bean + AzureEventHubsProperties azureEventHubsProperties() { + AzureEventHubsProperties propertiesLoadFromGlobalProperties = + AzureGlobalPropertiesUtils.loadProperties(globalProperties, new AzureEventHubsProperties()); + BindResult bindResult = Binder.get(environment) + .bind(AzureEventHubsProperties.PREFIX, Bindable.ofInstance(propertiesLoadFromGlobalProperties)); + AzureEventHubsProperties properties = bindResult.isBound() ? bindResult.get() + : propertiesLoadFromGlobalProperties; + properties.setConnectionString(connectionDetails.getConnectionString()); + return properties; + } + +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithoutConnectionDetailsBean.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithoutConnectionDetailsBean.java new file mode 100644 index 000000000000..9e0dbc1537c2 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithoutConnectionDetailsBean.java @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties; + +import com.azure.spring.cloud.autoconfigure.implementation.condition.ConditionalOnAnyProperty; +import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.properties.utils.AzureGlobalPropertiesUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; + +@ConditionalOnMissingBean(type = "com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails") +@ConditionalOnProperty(value = "spring.cloud.azure.eventhubs.enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnAnyProperty(prefix = "spring.cloud.azure.eventhubs", name = {"connection-string", "namespace"}) +class ConfigurationWithoutConnectionDetailsBean { + + private final AzureGlobalProperties azureGlobalProperties; + + ConfigurationWithoutConnectionDetailsBean(AzureGlobalProperties azureGlobalProperties) { + this.azureGlobalProperties = azureGlobalProperties; + } + + @Bean + @ConditionalOnMissingBean + @ConfigurationProperties(AzureEventHubsProperties.PREFIX) + AzureEventHubsProperties azureEventHubsProperties() { + return AzureGlobalPropertiesUtils.loadProperties(azureGlobalProperties, new AzureEventHubsProperties()); + } + +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfiguration.java index 05ee2da18780..597cfed54acc 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfiguration.java @@ -4,8 +4,6 @@ package com.azure.spring.cloud.autoconfigure.implementation.servicebus; import com.azure.messaging.servicebus.ServiceBusClientBuilder; -import com.azure.spring.cloud.autoconfigure.implementation.AzureServiceConfigurationBase; -import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusPropertiesConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -26,10 +24,6 @@ AzureServiceBusConsumerClientConfiguration.class, AzureServiceBusProcessorClientConfiguration.class }) -public class AzureServiceBusAutoConfiguration extends AzureServiceConfigurationBase { - - AzureServiceBusAutoConfiguration(AzureGlobalProperties azureGlobalProperties) { - super(azureGlobalProperties); - } +public class AzureServiceBusAutoConfiguration { } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfiguration.java index 9002f082e0c6..166d6a6593d4 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfiguration.java @@ -4,6 +4,7 @@ package com.azure.spring.cloud.autoconfigure.implementation.servicebus; import com.azure.messaging.servicebus.ServiceBusClientBuilder; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusPropertiesConfiguration; import com.azure.spring.cloud.core.customizer.AzureServiceClientBuilderCustomizer; @@ -62,4 +63,14 @@ StaticConnectionStringProvider staticServiceBusConn this.serviceBusProperties.getConnectionString()); } + @Bean + @ConditionalOnBean(AzureServiceBusConnectionDetails.class) + @ConditionalOnMissingBean(value = AzureServiceType.ServiceBus.class, parameterizedContainer = ServiceConnectionStringProvider.class) + StaticConnectionStringProvider staticServiceBusConnectionDetailsConnectionStringProvider( + AzureServiceBusConnectionDetails connectionDetails) { + + return new StaticConnectionStringProvider<>(AzureServiceType.SERVICE_BUS, + connectionDetails.getConnectionString()); + } + } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfigurationTests.java index bfef43814749..656557e67ce2 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfigurationTests.java @@ -75,7 +75,7 @@ void configureWithEventHubDisabled() { void configureWithoutConnectionStringAndNamespace() { this.contextRunner .withPropertyValues("spring.cloud.azure.eventhubs.enabled=true") - .run(context -> assertThat(context).doesNotHaveBean(AzureEventHubsAutoConfiguration.class)); + .run(context -> assertThat(context).doesNotHaveBean(AzureEventHubsProperties.class)); } @Test @@ -277,4 +277,20 @@ void configurationPropertiesShouldBind() { }); } + @Test + void connectionDetailsOverridesPropertyConnectionString() { + String connectionString = String.format(CONNECTION_STRING_FORMAT, "test-namespace"); + this.contextRunner + .withPropertyValues( + "spring.cloud.azure.eventhubs.connection-string=" + connectionString + ) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) + .withBean(com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails.class, CustomAzureEventHubsConnectionDetails::new) + .run(context -> { + assertThat(context).hasSingleBean(AzureEventHubsProperties.class); + AzureEventHubsProperties properties = context.getBean(AzureEventHubsProperties.class); + assertEquals(CustomAzureEventHubsConnectionDetails.CONNECTION_STRING, properties.getConnectionString()); + }); + } + } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsClientBuilderConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsClientBuilderConfigurationTests.java index d4b92480a714..62d554eb6741 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsClientBuilderConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsClientBuilderConfigurationTests.java @@ -6,6 +6,9 @@ import com.azure.data.appconfiguration.ConfigurationClientBuilder; import com.azure.messaging.eventhubs.EventHubClientBuilder; import com.azure.spring.cloud.autoconfigure.implementation.TestBuilderCustomizer; +import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails; +import com.azure.spring.cloud.core.provider.connectionstring.StaticConnectionStringProvider; import com.azure.spring.cloud.service.implementation.eventhubs.factory.EventHubClientBuilderFactory; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -17,7 +20,7 @@ class AzureEventHubsClientBuilderConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(AzureEventHubsClientBuilderConfiguration.class)); + .withConfiguration(AutoConfigurations.of(AzureEventHubsAutoConfiguration.class)); @Test void noConnectionInfoProvidedShouldNotConfigure() { @@ -30,7 +33,7 @@ void connectionStringProvidedShouldConfigure() { .withPropertyValues( "spring.cloud.azure.eventhubs.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace") ) - .withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) .run(context -> { assertThat(context).hasSingleBean(AzureEventHubsClientBuilderConfiguration.class); assertThat(context).hasSingleBean(EventHubClientBuilderFactory.class); @@ -44,7 +47,7 @@ void namespaceProvidedShouldConfigure() { .withPropertyValues( "spring.cloud.azure.eventhubs.namespace=test-namespace" ) - .withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) .run(context -> { assertThat(context).hasSingleBean(AzureEventHubsClientBuilderConfiguration.class); assertThat(context).hasSingleBean(EventHubClientBuilderFactory.class); @@ -59,7 +62,7 @@ void customizerShouldBeCalled() { .withPropertyValues( "spring.cloud.azure.eventhubs.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace") ) - .withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) .withBean("customizer1", EventHubBuilderCustomizer.class, () -> customizer) .withBean("customizer2", EventHubBuilderCustomizer.class, () -> customizer) .run(context -> assertThat(customizer.getCustomizedTimes()).isEqualTo(2)); @@ -73,7 +76,7 @@ void otherCustomizerShouldNotBeCalled() { .withPropertyValues( "spring.cloud.azure.eventhubs.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace") ) - .withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) .withBean("customizer1", EventHubBuilderCustomizer.class, () -> customizer) .withBean("customizer2", EventHubBuilderCustomizer.class, () -> customizer) .withBean("customizer3", OtherBuilderCustomizer.class, () -> otherBuilderCustomizer) @@ -89,7 +92,7 @@ void userDefinedEventHubsClientBuilderProvidedShouldNotAutoconfigure() { .withPropertyValues( "spring.cloud.azure.eventhubs.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace") ) - .withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) .withBean("user-defined-builder", EventHubClientBuilder.class, EventHubClientBuilder::new) .run(context -> { assertThat(context).hasSingleBean(EventHubClientBuilder.class); @@ -97,6 +100,44 @@ void userDefinedEventHubsClientBuilderProvidedShouldNotAutoconfigure() { }); } + @Test + void connectionStringPropertyRegistersStaticProvider() { + String connectionString = String.format(CONNECTION_STRING_FORMAT, "test-namespace"); + this.contextRunner + .withPropertyValues( + "spring.cloud.azure.eventhubs.connection-string=" + connectionString + ) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) + .run(context -> { + assertThat(context).hasSingleBean(StaticConnectionStringProvider.class); + assertThat(context.getBean(StaticConnectionStringProvider.class).getConnectionString()) + .isEqualTo(connectionString); + }); + } + + @Test + void connectionDetailsRegistersStaticProvider() { + String connectionString = String.format(CONNECTION_STRING_FORMAT, "details-namespace"); + this.contextRunner + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) + .withBean(AzureEventHubsConnectionDetails.class, () -> new TestConnectionDetails(connectionString)) + .run(context -> { + assertThat(context).hasSingleBean(StaticConnectionStringProvider.class); + assertThat(context.getBean(StaticConnectionStringProvider.class).getConnectionString()) + .isEqualTo(connectionString); + }); + } + + @Test + void namespaceOnlyDoesNotRegisterStaticProvider() { + this.contextRunner + .withPropertyValues( + "spring.cloud.azure.eventhubs.namespace=test-namespace" + ) + .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) + .run(context -> assertThat(context).doesNotHaveBean(StaticConnectionStringProvider.class)); + } + private static class EventHubBuilderCustomizer extends TestBuilderCustomizer { } @@ -105,4 +146,17 @@ private static class OtherBuilderCustomizer extends TestBuilderCustomizer { assertThat(context).hasSingleBean(AzureEventHubsConsumerClientConfiguration.class); assertThat(context).doesNotHaveBean(AzureEventHubsConsumerClientConfiguration.SharedConsumerConnectionConfiguration.class); @@ -45,6 +46,7 @@ void eventHubNameAndConsumerGroupProvidedShouldConfigure() { "spring.cloud.azure.eventhubs.consumer.event-hub-name=test-eventhub", "spring.cloud.azure.eventhubs.consumer.consumer-group=test-consumer-group" ) + .withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class) .run(context -> { assertThat(context).hasSingleBean(AzureEventHubsConsumerClientConfiguration.class); assertThat(context).doesNotHaveBean(AzureEventHubsConsumerClientConfiguration.SharedConsumerConnectionConfiguration.class); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsMessagingAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsMessagingAutoConfigurationTests.java index 87daf3997d50..6939088dba23 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsMessagingAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsMessagingAutoConfigurationTests.java @@ -58,7 +58,6 @@ void withoutEventHubsTemplateShouldNotConfigure() { @Test void withoutEventHubConnectionShouldNotConfigure() { this.contextRunner - .withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class) .withBean(CheckpointStore.class, TestCheckpointStore::new) .run(context -> assertThat(context).doesNotHaveBean(AzureEventHubsMessagingAutoConfiguration.ProcessorContainerConfiguration.class)); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProducerClientConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProducerClientConfigurationTests.java index d8c4f6147c89..db7361326694 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProducerClientConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsProducerClientConfigurationTests.java @@ -33,6 +33,7 @@ void eventHubNameProvidedShouldConfigure() { .withPropertyValues( "spring.cloud.azure.eventhubs.event-hub-name=test-eventhub" ) + .withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class) .run(context -> { assertThat(context).hasSingleBean(AzureEventHubsProducerClientConfiguration.class); assertThat(context).doesNotHaveBean(AzureEventHubsProducerClientConfiguration.SharedProducerConnectionConfiguration.class); @@ -43,6 +44,7 @@ void eventHubNameProvidedShouldConfigure() { .withPropertyValues( "spring.cloud.azure.eventhubs.producer.event-hub-name=test-eventhub" ) + .withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class) .run(context -> { assertThat(context).hasSingleBean(AzureEventHubsProducerClientConfiguration.class); assertThat(context).doesNotHaveBean(AzureEventHubsProducerClientConfiguration.SharedProducerConnectionConfiguration.class); @@ -65,6 +67,7 @@ void withGlobalEventHubConnectionSetShouldConfigureShared() { "spring.cloud.azure.eventhubs.namespace=" + namespace, "spring.cloud.azure.eventhubs.event-hub-name=" + eventHubName ) + .withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class) .withBean(EventHubClientBuilder.class, () -> clientBuilder) .run( context -> { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/CustomAzureEventHubsConnectionDetails.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/CustomAzureEventHubsConnectionDetails.java new file mode 100644 index 000000000000..d5faa229ef11 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/CustomAzureEventHubsConnectionDetails.java @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.eventhubs; + +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails; + +public class CustomAzureEventHubsConnectionDetails implements AzureEventHubsConnectionDetails { + + static final String CONNECTION_STRING = "Endpoint=sb://connection-detail-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=test-key;EntityPath=test-eventhub"; + + @Override + public String getConnectionString() { + return CONNECTION_STRING; + } +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaAutoConfigurationTests.java index 8eb24d6d593e..f8e200454794 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaAutoConfigurationTests.java @@ -5,6 +5,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.AzureEventHubsAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails; import com.azure.spring.cloud.core.provider.connectionstring.StaticConnectionStringProvider; import com.azure.spring.cloud.core.service.AzureServiceType; import com.azure.spring.cloud.resourcemanager.implementation.connectionstring.ArmConnectionStringProvider; @@ -26,12 +27,12 @@ import static org.apache.kafka.common.security.auth.SecurityProtocol.SASL_SSL; import static org.assertj.core.api.Assertions.assertThat; - @SuppressWarnings("deprecation") class AzureEventHubsKafkaAutoConfigurationTests { static final String CONNECTION_STRING_FORMAT = "Endpoint=sb://%s.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=key"; + private static final String CONNECTION_STRING = String.format(CONNECTION_STRING_FORMAT, "test-namespace"); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(AzureEventHubsKafkaAutoConfiguration.class, KafkaAutoConfiguration.class)); @@ -223,5 +224,36 @@ public AzureServiceType.EventHubs getServiceType() { } } + @Test + void connectionStringRegistersProvider() { + this.contextRunner + .withPropertyValues( + "spring.cloud.azure.eventhubs.connection-string=" + CONNECTION_STRING + ) + .run(context -> { + assertThat(context).hasSingleBean(StaticConnectionStringProvider.class); + StaticConnectionStringProvider provider = context.getBean(StaticConnectionStringProvider.class); + assertThat(provider.getServiceType()).isEqualTo(AzureServiceType.EVENT_HUBS); + assertThat(provider.getConnectionString()).isEqualTo(CONNECTION_STRING); + }); + } + @Test + void connectionDetailsRegistersProvider() { + this.contextRunner + .withBean(AzureEventHubsConnectionDetails.class, () -> () -> CONNECTION_STRING) + .run(context -> { + assertThat(context).hasSingleBean(StaticConnectionStringProvider.class); + StaticConnectionStringProvider provider = context.getBean(StaticConnectionStringProvider.class); + assertThat(provider.getServiceType()).isEqualTo(AzureServiceType.EVENT_HUBS); + assertThat(provider.getConnectionString()).isEqualTo(CONNECTION_STRING); + }); + } + + @Test + void namespaceOnlyDoesNotRegisterProvider() { + this.contextRunner + .withPropertyValues("spring.cloud.azure.eventhubs.namespace=test-namespace") + .run(context -> assertThat(context).doesNotHaveBean(StaticConnectionStringProvider.class)); + } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfigurationTests.java index 83c1f2d1ed47..cde00bb3389c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusClientBuilderConfigurationTests.java @@ -7,6 +7,7 @@ import com.azure.messaging.servicebus.ServiceBusClientBuilder; import com.azure.spring.cloud.autoconfigure.implementation.TestBuilderCustomizer; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; import com.azure.spring.cloud.core.provider.connectionstring.StaticConnectionStringProvider; import com.azure.spring.cloud.core.service.AzureServiceType; @@ -31,7 +32,7 @@ void noConnectionInfoProvidedShouldNotConfigure() { } @Test - @SuppressWarnings("rawtypes") + @SuppressWarnings({"rawtypes", "unchecked"}) void connectionStringProvidedShouldConfigure() { contextRunner .withPropertyValues( @@ -89,6 +90,20 @@ void configureWithNamespaceAndEmptyConnectionString() { }); } + @Test + @SuppressWarnings({"rawtypes", "unchecked"}) + void connectionDetailsRegistersStaticProvider() { + String connectionString = String.format(CONNECTION_STRING_FORMAT, "details-namespace"); + this.contextRunner + .withBean(AzureServiceBusConnectionDetails.class, () -> new TestConnectionDetails(connectionString)) + .run(context -> { + assertThat(context).hasSingleBean(StaticConnectionStringProvider.class); + StaticConnectionStringProvider provider = context.getBean(StaticConnectionStringProvider.class); + assertThat(provider.getConnectionString()).isEqualTo(connectionString); + assertThat(provider.getServiceType()).isEqualTo(AzureServiceType.SERVICE_BUS); + }); + } + private static class ServiceBusBuilderCustomizer extends TestBuilderCustomizer { } @@ -97,4 +112,17 @@ private static class OtherBuilderCustomizer extends TestBuilderCustomizer12.26.1 true - - com.azure - azure-messaging-servicebus - 7.17.17 - true - org.springframework.boot @@ -99,6 +93,18 @@ provided + + com.azure + azure-messaging-servicebus + 7.17.17 + test + + + com.azure + azure-messaging-eventhubs + 5.21.3 + test + com.azure.spring spring-messaging-azure-servicebus diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/main/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/EventHubsDockerComposeConnectionDetailsFactory.java b/sdk/spring/spring-cloud-azure-docker-compose/src/main/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/EventHubsDockerComposeConnectionDetailsFactory.java new file mode 100644 index 000000000000..e34128b855f5 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/main/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/EventHubsDockerComposeConnectionDetailsFactory.java @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.docker.compose.implementation.service.connection.hubs; + +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails; +import org.springframework.boot.docker.compose.core.RunningService; +import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; +import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource; + +class EventHubsDockerComposeConnectionDetailsFactory + extends DockerComposeConnectionDetailsFactory { + + private static final int EVENT_HUBS_PORT = 5672; + + protected EventHubsDockerComposeConnectionDetailsFactory() { + super("azure-messaging/eventhubs-emulator"); + } + + @Override + protected AzureEventHubsConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) { + return new EventHubsContainerConnectionDetails(source.getRunningService()); + } + + /** + * {@link AzureEventHubsConnectionDetails} backed by an {@code Event Hubs} + * {@link RunningService}. + */ + private static class EventHubsContainerConnectionDetails extends DockerComposeConnectionDetails + implements AzureEventHubsConnectionDetails { + + private final String host; + + private final int port; + + EventHubsContainerConnectionDetails(RunningService service) { + super(service); + this.host = service.host(); + this.port = service.ports().get(EVENT_HUBS_PORT); + } + + @Override + public String getConnectionString() { + return "Endpoint=sb://%s:%d;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;" + .formatted(this.host, this.port); + } + } +} diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/main/resources/META-INF/spring.factories b/sdk/spring/spring-cloud-azure-docker-compose/src/main/resources/META-INF/spring.factories index 6de8408e5b64..591a4d528944 100644 --- a/sdk/spring/spring-cloud-azure-docker-compose/src/main/resources/META-INF/spring.factories +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\ com.azure.spring.cloud.docker.compose.implementation.service.connection.bus.ServiceBusDockerComposeConnectionDetailsFactory,\ com.azure.spring.cloud.docker.compose.implementation.service.connection.storage.StorageBlobDockerComposeConnectionDetailsFactory,\ -com.azure.spring.cloud.docker.compose.implementation.service.connection.storage.StorageQueueDockerComposeConnectionDetailsFactory +com.azure.spring.cloud.docker.compose.implementation.service.connection.storage.StorageQueueDockerComposeConnectionDetailsFactory,\ +com.azure.spring.cloud.docker.compose.implementation.service.connection.hubs.EventHubsDockerComposeConnectionDetailsFactory diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactoryTests.java b/sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactoryTests.java index 9a14f229e914..3f1d51308a7d 100644 --- a/sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactoryTests.java +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/ServiceBusDockerComposeConnectionDetailsFactoryTests.java @@ -8,6 +8,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusMessagingAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails; import com.azure.spring.cloud.service.servicebus.consumer.ServiceBusErrorHandler; import com.azure.spring.cloud.service.servicebus.consumer.ServiceBusRecordMessageListener; import com.azure.spring.messaging.servicebus.core.ServiceBusTemplate; @@ -30,8 +31,9 @@ @SpringBootTest(properties = { "spring.docker.compose.skip.in-tests=false", - "spring.docker.compose.file=classpath:com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/servicebus-compose.yaml", + "spring.docker.compose.file=classpath:com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/servicebus-compose.yaml", "spring.docker.compose.stop.command=down", + "spring.docker.compose.readiness.timeout=PT5M", "spring.cloud.azure.servicebus.namespace=sbemulatorns", "spring.cloud.azure.servicebus.entity-name=queue.1", "spring.cloud.azure.servicebus.entity-type=queue", @@ -43,12 +45,23 @@ @EnabledOnOs(OS.LINUX) class ServiceBusDockerComposeConnectionDetailsFactoryTests { + @Autowired + private AzureServiceBusConnectionDetails connectionDetails; + @Autowired private ServiceBusSenderClient senderClient; @Autowired private ServiceBusTemplate serviceBusTemplate; + @Test + void connectionDetailsShouldBeProvidedByFactory() { + assertThat(connectionDetails).isNotNull(); + assertThat(connectionDetails.getConnectionString()) + .isNotBlank() + .startsWith("Endpoint=sb://"); + } + @Test void senderClientCanSendMessage() { // Wait for Service Bus emulator to be fully ready and queue entity to be available diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/EventHubsDockerComposeConnectionDetailsFactoryTests.java b/sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/EventHubsDockerComposeConnectionDetailsFactoryTests.java new file mode 100644 index 000000000000..1369301024c1 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/test/java/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/EventHubsDockerComposeConnectionDetailsFactoryTests.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.docker.compose.implementation.service.connection.hubs; + +import com.azure.messaging.eventhubs.EventData; +import com.azure.messaging.eventhubs.EventHubProducerClient; +import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.AzureEventHubsAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; + +import java.time.Duration; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.waitAtMost; + +@SpringBootTest(properties = { + "spring.docker.compose.skip.in-tests=false", + "spring.docker.compose.file=classpath:com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/eventhubs-compose.yaml", + "spring.docker.compose.stop.command=down", + "spring.docker.compose.readiness.timeout=PT5M", + "spring.cloud.azure.eventhubs.event-hub-name=eh1", + "spring.cloud.azure.eventhubs.producer.event-hub-name=eh1" +}) +@EnabledOnOs(OS.LINUX) +class EventHubsDockerComposeConnectionDetailsFactoryTests { + + @Autowired + private AzureEventHubsConnectionDetails connectionDetails; + + @Autowired + private EventHubProducerClient producerClient; + + @Test + void connectionDetailsShouldBeProvidedByFactory() { + assertThat(connectionDetails).isNotNull(); + assertThat(connectionDetails.getConnectionString()) + .isNotBlank() + .startsWith("Endpoint=sb://"); + } + + @Test + void producerClientCanSendMessage() { + // Wait for Event Hubs emulator to be fully ready and event hub entity to be available + waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -> { + EventData event = new EventData("Hello World!"); + this.producerClient.send(Collections.singletonList(event)); + }); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(classes = { + AzureGlobalPropertiesAutoConfiguration.class, + AzureEventHubsAutoConfiguration.class}) + static class Config { + } +} diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/Config.json b/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/Config.json similarity index 100% rename from sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/Config.json rename to sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/Config.json diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/servicebus-compose.yaml b/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/servicebus-compose.yaml similarity index 100% rename from sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/servicebus/servicebus-compose.yaml rename to sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/bus/servicebus-compose.yaml diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/Config.json b/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/Config.json new file mode 100644 index 000000000000..0749990d11d4 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/Config.json @@ -0,0 +1,20 @@ +{ + "UserConfig": { + "NamespaceConfig": [ + { + "Type": "EventHub", + "Name": "emulatorns1", + "Entities": [ + { + "Name": "eh1", + "PartitionCount": "2", + "ConsumerGroups": [] + } + ] + } + ], + "LoggingConfig": { + "Type": "File" + } + } +} diff --git a/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/eventhubs-compose.yaml b/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/eventhubs-compose.yaml new file mode 100644 index 000000000000..6aa1d2d7a0f9 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-docker-compose/src/test/resources/com/azure/spring/cloud/docker/compose/implementation/service/connection/hubs/eventhubs-compose.yaml @@ -0,0 +1,34 @@ +services: + eventhubs: + image: mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest + pull_policy: always + volumes: + # Mount the emulator configuration to the path expected by the emulator image + - "./Config.json:/Eventhubs_Emulator/ConfigFiles/Config.json" + ports: + - "5672" + environment: + # Event Hubs emulator requires external blob/metadata storage provided by azurite + BLOB_SERVER: azurite + METADATA_SERVER: azurite + ACCEPT_EULA: Y + depends_on: + - azurite + networks: + eh-emulator: + aliases: + - "eh-emulator" + + azurite: + image: "mcr.microsoft.com/azure-storage/azurite:latest" + ports: + - "10000" + - "10001" + - "10002" + networks: + eh-emulator: + aliases: + - "azurite" + +networks: + eh-emulator: diff --git a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml index 9beab539ced0..58409b6730c5 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml +++ b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml @@ -78,12 +78,6 @@ 12.26.1 true - - com.azure - azure-messaging-servicebus - 7.17.13 - true - com.azure.spring spring-messaging-azure-servicebus @@ -102,12 +96,6 @@ 1.21.3 true - - com.microsoft.sqlserver - mssql-jdbc - 13.2.1.jre11 - true - @@ -118,6 +106,24 @@ provided + + com.azure + azure-messaging-servicebus + 7.17.13 + test + + + com.azure + azure-messaging-eventhubs + 5.20.5 + test + + + com.microsoft.sqlserver + mssql-jdbc + 13.2.1.jre11 + test + org.springframework spring-test diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/main/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/hubs/EventHubsContainerConnectionDetailsFactory.java b/sdk/spring/spring-cloud-azure-testcontainers/src/main/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/hubs/EventHubsContainerConnectionDetailsFactory.java new file mode 100644 index 000000000000..a3b5460446ef --- /dev/null +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/main/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/hubs/EventHubsContainerConnectionDetailsFactory.java @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.testcontainers.implementation.service.connection.hubs; + +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource; +import org.testcontainers.azure.EventHubsEmulatorContainer; + +class EventHubsContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory { + + @Override + protected AzureEventHubsConnectionDetails getContainerConnectionDetails(ContainerConnectionSource source) { + return new EventHubsContainerConnectionDetails(source); + } + + /** + * {@link AzureEventHubsConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static class EventHubsContainerConnectionDetails extends ContainerConnectionDetails + implements AzureEventHubsConnectionDetails { + + EventHubsContainerConnectionDetails(ContainerConnectionSource source) { + super(source); + } + + @Override + public String getConnectionString() { + return getContainer().getConnectionString(); + } + } +} diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/main/resources/META-INF/spring.factories b/sdk/spring/spring-cloud-azure-testcontainers/src/main/resources/META-INF/spring.factories index 8d3008a6217a..a590e8a7e19f 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/src/main/resources/META-INF/spring.factories +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/main/resources/META-INF/spring.factories @@ -2,4 +2,5 @@ org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFacto com.azure.spring.cloud.testcontainers.implementation.service.connection.cosmos.CosmosContainerConnectionDetailsFactory,\ com.azure.spring.cloud.testcontainers.implementation.service.connection.bus.ServiceBusContainerConnectionDetailsFactory,\ com.azure.spring.cloud.testcontainers.implementation.service.connection.storage.StorageBlobContainerConnectionDetailsFactory,\ -com.azure.spring.cloud.testcontainers.implementation.service.connection.storage.StorageQueueContainerConnectionDetailsFactory +com.azure.spring.cloud.testcontainers.implementation.service.connection.storage.StorageQueueContainerConnectionDetailsFactory,\ +com.azure.spring.cloud.testcontainers.implementation.service.connection.hubs.EventHubsContainerConnectionDetailsFactory diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java b/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java index 0771ab5da8a4..de67d4779f35 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java @@ -8,6 +8,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusMessagingAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails; import com.azure.spring.cloud.service.servicebus.consumer.ServiceBusErrorHandler; import com.azure.spring.cloud.service.servicebus.consumer.ServiceBusRecordMessageListener; import com.azure.spring.messaging.servicebus.core.ServiceBusTemplate; @@ -61,12 +62,23 @@ class ServiceBusContainerConnectionDetailsFactoryTests { .withNetwork(NETWORK) .withMsSqlServerContainer(SQLSERVER); + @Autowired + private AzureServiceBusConnectionDetails connectionDetails; + @Autowired private ServiceBusSenderClient senderClient; @Autowired private ServiceBusTemplate serviceBusTemplate; + @Test + void connectionDetailsShouldBeProvidedByFactory() { + assertThat(connectionDetails).isNotNull(); + assertThat(connectionDetails.getConnectionString()) + .isNotBlank() + .startsWith("Endpoint=sb://"); + } + @Test void senderClientCanSendMessage() { // Wait for Service Bus emulator to be fully ready and queue entity to be available diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/hubs/EventHubsContainerConnectionDetailsFactoryTests.java b/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/hubs/EventHubsContainerConnectionDetailsFactoryTests.java new file mode 100644 index 000000000000..84c05c2bc6a2 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/hubs/EventHubsContainerConnectionDetailsFactoryTests.java @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.testcontainers.implementation.service.connection.hubs; + +import com.azure.messaging.eventhubs.EventData; +import com.azure.messaging.eventhubs.EventHubProducerClient; +import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.AzureEventHubsAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsConnectionDetails; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import org.testcontainers.azure.EventHubsEmulatorContainer; +import org.testcontainers.azure.AzuriteContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.MountableFile; + +import java.time.Duration; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.waitAtMost; + +@SpringJUnitConfig +@TestPropertySource(properties = { + "spring.cloud.azure.eventhubs.event-hub-name=eh1" +}) +@Testcontainers +@EnabledOnOs(OS.LINUX) +class EventHubsContainerConnectionDetailsFactoryTests { + + private static final Network NETWORK = Network.newNetwork(); + + @Container + private static final AzuriteContainer AZURITE = new AzuriteContainer("mcr.microsoft.com/azure-storage/azurite:latest") + .withNetwork(NETWORK) + .withNetworkAliases("azurite"); + + @Container + @ServiceConnection + private static final EventHubsEmulatorContainer EVENT_HUBS = new EventHubsEmulatorContainer( + "mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest") + .acceptLicense() + .withCopyFileToContainer(MountableFile.forClasspathResource("eventhubs/Config.json"), + "/Eventhubs_Emulator/ConfigFiles/Config.json") + .withNetwork(NETWORK) + .withAzuriteContainer(AZURITE); + + @Autowired + private AzureEventHubsConnectionDetails connectionDetails; + + @Autowired + private EventHubProducerClient producerClient; + + @Test + void connectionDetailsShouldBeProvidedByFactory() { + assertThat(connectionDetails).isNotNull(); + assertThat(connectionDetails.getConnectionString()) + .isNotBlank() + .startsWith("Endpoint=sb://"); + } + + @Test + void producerClientCanSendMessage() { + // Wait for Event Hubs emulator to be fully ready and event hub entity to be available + waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -> { + EventData event = new EventData("Hello World!"); + this.producerClient.send(Collections.singletonList(event)); + }); + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(classes = {AzureGlobalPropertiesAutoConfiguration.class, + AzureEventHubsAutoConfiguration.class}) + static class Config { + } +} diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/eventhubs/Config.json b/sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/eventhubs/Config.json new file mode 100644 index 000000000000..0749990d11d4 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/test/resources/eventhubs/Config.json @@ -0,0 +1,20 @@ +{ + "UserConfig": { + "NamespaceConfig": [ + { + "Type": "EventHub", + "Name": "emulatorns1", + "Entities": [ + { + "Name": "eh1", + "PartitionCount": "2", + "ConsumerGroups": [] + } + ] + } + ], + "LoggingConfig": { + "Type": "File" + } + } +} From 26b6b072fc76bbb05b290b11c50f9c2fbe3dc377 Mon Sep 17 00:00:00 2001 From: muyao Date: Sat, 14 Feb 2026 09:32:31 +0800 Subject: [PATCH 03/22] Fix --- sdk/spring/spring-cloud-azure-docker-compose/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-docker-compose/pom.xml b/sdk/spring/spring-cloud-azure-docker-compose/pom.xml index e4680b9617ad..fe468c9da775 100644 --- a/sdk/spring/spring-cloud-azure-docker-compose/pom.xml +++ b/sdk/spring/spring-cloud-azure-docker-compose/pom.xml @@ -96,19 +96,19 @@ com.azure azure-messaging-servicebus - 7.17.17 + 7.17.13 test com.azure azure-messaging-eventhubs - 5.21.3 + 5.20.5 test com.azure.spring spring-messaging-azure-servicebus - 7.1.0-beta.1 + 5.24.1 test @@ -138,7 +138,7 @@ org.awaitility awaitility - 4.3.0 + 4.3.0 test From 4162e63873b3f09dfd6550aa297991cb49ccdf69 Mon Sep 17 00:00:00 2001 From: muyao Date: Fri, 27 Feb 2026 09:11:29 +0800 Subject: [PATCH 04/22] Fix err: Could not transfer artifact org.bouncycastle:bcutil-lts8on:jar:2.73.10 from/to azure-sdk-for-java (https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-java/maven/v1): status code: 401, reason phrase: Unauthorized - No local versions of package 'org.bouncycastle:bcutil-lts8on'; please provide authentication to access versions from upstream that have not yet been saved to your feed --- eng/versioning/external_dependencies.txt | 2 +- sdk/keyvault/azure-security-keyvault-certificates/pom.xml | 2 +- sdk/keyvault/azure-security-keyvault-jca/pom.xml | 4 ++-- sdk/keyvault/cgmanifest.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index df323165167f..d4c5353ac9ca 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -141,7 +141,7 @@ commons-cli:commons-cli;1.9.0 org.assertj:assertj-core;3.22.0 org.bouncycastle:bcprov-jdk15to18;1.78.1 org.bouncycastle:bcprov-jdk18on;1.78.1 -org.bouncycastle:bcpkix-lts8on;2.73.6 +org.bouncycastle:bcpkix-lts8on;2.73.8 org.eclipse.jetty:jetty-alpn-conscrypt-server;9.4.57.v20241219 org.eclipse.jetty:jetty-server;9.4.57.v20241219 org.eclipse.jetty:jetty-servlet;9.4.57.v20241219 diff --git a/sdk/keyvault/azure-security-keyvault-certificates/pom.xml b/sdk/keyvault/azure-security-keyvault-certificates/pom.xml index fcff7bd24233..e88241df2d9a 100644 --- a/sdk/keyvault/azure-security-keyvault-certificates/pom.xml +++ b/sdk/keyvault/azure-security-keyvault-certificates/pom.xml @@ -79,7 +79,7 @@ org.bouncycastle bcpkix-lts8on - 2.73.6 + 2.73.8 test diff --git a/sdk/keyvault/azure-security-keyvault-jca/pom.xml b/sdk/keyvault/azure-security-keyvault-jca/pom.xml index ed2c196d8cdb..8dcaff887991 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/pom.xml +++ b/sdk/keyvault/azure-security-keyvault-jca/pom.xml @@ -32,7 +32,7 @@ org.bouncycastle bcpkix-lts8on - 2.73.6 + 2.73.8 true @@ -274,7 +274,7 @@ - org.bouncycastle:bcpkix-lts8on:[2.73.6] + org.bouncycastle:bcpkix-lts8on:[2.73.8] org.conscrypt:conscrypt-openjdk-uber:[2.5.2] org.apache.httpcomponents.client5:httpclient5:[5.4.3] org.brotli:dec:[0.1.2] diff --git a/sdk/keyvault/cgmanifest.json b/sdk/keyvault/cgmanifest.json index 6aa24615838e..cc6451937e25 100644 --- a/sdk/keyvault/cgmanifest.json +++ b/sdk/keyvault/cgmanifest.json @@ -9,7 +9,7 @@ "maven": { "groupId": "org.bouncycastle", "artifactId": "bcpkix-lts8on", - "version": "2.73.6" + "version": "2.73.8" } } } From 021f4905a39fef1752ca4c3a7f4ae7c358671e37 Mon Sep 17 00:00:00 2001 From: muyao Date: Fri, 27 Feb 2026 09:36:10 +0800 Subject: [PATCH 05/22] Fix cspell --- .vscode/cspell.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/cspell.json b/.vscode/cspell.json index 3eebb52c6caa..fcd3051c177f 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -1026,6 +1026,7 @@ "awaitility", "awspring", "bcfips", + "bcpkix", "Borca", "brotli", "bson", From 50e26c55851ceed08c82efee8403ebf7af547205 Mon Sep 17 00:00:00 2001 From: muyao Date: Fri, 27 Feb 2026 10:17:55 +0800 Subject: [PATCH 06/22] update testcontainers to 2.0.2 to fix error: Can't get Docker image --- eng/versioning/external_dependencies.txt | 1 + sdk/spring/spring-cloud-azure-testcontainers/pom.xml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index d4c5353ac9ca..46b112e9bbd6 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -413,6 +413,7 @@ springboot3_org.springframework:spring-web;6.2.9 springboot3_org.springframework:spring-webmvc;6.2.9 springboot3_org.testcontainers:junit-jupiter;1.21.3 springboot3_org.testcontainers:azure;1.21.3 +springboot3_org.testcontainers:testcontainers;2.0.2 springboot3_org.awaitility:awaitility;4.3.0 springboot3_com.microsoft.sqlserver:mssql-jdbc;13.2.1.jre11 # Used for Spring version updates diff --git a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml index 58409b6730c5..9daab2982f14 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml +++ b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml @@ -90,6 +90,11 @@ spring-boot-testcontainers 3.5.4 + + org.testcontainers + testcontainers + 2.0.2 + org.testcontainers azure @@ -199,6 +204,7 @@ org.springframework.boot:spring-boot-testcontainers:[3.5.4] org.testcontainers:azure:[1.21.3] + org.testcontainers:testcontainers:[1.21.3] com.microsoft.sqlserver:mssql-jdbc:[13.2.1.jre11] From 540b408b73e1942eb30c3b2b1b80583e2800e245 Mon Sep 17 00:00:00 2001 From: muyao Date: Fri, 27 Feb 2026 11:23:12 +0800 Subject: [PATCH 07/22] fix --- sdk/spring/spring-cloud-azure-testcontainers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml index 9daab2982f14..5e34e262950a 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml +++ b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml @@ -204,7 +204,7 @@ org.springframework.boot:spring-boot-testcontainers:[3.5.4] org.testcontainers:azure:[1.21.3] - org.testcontainers:testcontainers:[1.21.3] + org.testcontainers:testcontainers:[2.0.2] com.microsoft.sqlserver:mssql-jdbc:[13.2.1.jre11] From 31970e7bfbffc1c7258e8d4f3258e13528088785 Mon Sep 17 00:00:00 2001 From: muyao Date: Fri, 27 Feb 2026 13:05:26 +0800 Subject: [PATCH 08/22] fix --- .vscode/cspell.json | 1 - sdk/keyvault/cgmanifest.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/cspell.json b/.vscode/cspell.json index fcd3051c177f..3eebb52c6caa 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -1026,7 +1026,6 @@ "awaitility", "awspring", "bcfips", - "bcpkix", "Borca", "brotli", "bson", diff --git a/sdk/keyvault/cgmanifest.json b/sdk/keyvault/cgmanifest.json index cc6451937e25..6aa24615838e 100644 --- a/sdk/keyvault/cgmanifest.json +++ b/sdk/keyvault/cgmanifest.json @@ -9,7 +9,7 @@ "maven": { "groupId": "org.bouncycastle", "artifactId": "bcpkix-lts8on", - "version": "2.73.8" + "version": "2.73.6" } } } From 12d3427cdacc72fa7ac753cf4ee505dafd0457b9 Mon Sep 17 00:00:00 2001 From: muyao Date: Fri, 27 Feb 2026 14:16:10 +0800 Subject: [PATCH 09/22] fix compatibility tests --- sdk/spring/scripts/compatibility_delete_version.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sdk/spring/scripts/compatibility_delete_version.py b/sdk/spring/scripts/compatibility_delete_version.py index e5b59474c2bf..7cc7e2a7eb49 100644 --- a/sdk/spring/scripts/compatibility_delete_version.py +++ b/sdk/spring/scripts/compatibility_delete_version.py @@ -30,6 +30,18 @@ SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "net.bytebuddy:byte-buddy", SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "net.bytebuddy:byte-buddy-agent", SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.mockito:mockito-core" + }, + "3.1.12": { + SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:azure", + SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:testcontainers" + }, + "3.2.12": { + SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:azure", + SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:testcontainers" + }, + "3.3.13": { + SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:azure", + SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:testcontainers" } } From 8489d26f61c9beb05c7c310dfff94f838fedbc5d Mon Sep 17 00:00:00 2001 From: muyao Date: Sat, 28 Feb 2026 10:42:33 +0800 Subject: [PATCH 10/22] update org.apache.commons:commons-lang3 to fix error: NoClassDefFound org/apache/commons/lang3/ArrayFill --- sdk/spring/scripts/compatibility_delete_version.py | 2 ++ sdk/spring/spring-cloud-azure-testcontainers/pom.xml | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/sdk/spring/scripts/compatibility_delete_version.py b/sdk/spring/scripts/compatibility_delete_version.py index 7cc7e2a7eb49..7eac213e8e1e 100644 --- a/sdk/spring/scripts/compatibility_delete_version.py +++ b/sdk/spring/scripts/compatibility_delete_version.py @@ -36,10 +36,12 @@ SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:testcontainers" }, "3.2.12": { + SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.apache.commons:commons-lang3", SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:azure", SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:testcontainers" }, "3.3.13": { + SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.apache.commons:commons-lang3", SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:azure", SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:testcontainers" } diff --git a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml index 5e34e262950a..472ab6e49c68 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml +++ b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml @@ -90,6 +90,11 @@ spring-boot-testcontainers 3.5.4 + + org.apache.commons + commons-lang3 + 3.17.0 + org.testcontainers testcontainers @@ -202,6 +207,7 @@ + org.apache.commons:commons-lang3:[3.17.0] org.springframework.boot:spring-boot-testcontainers:[3.5.4] org.testcontainers:azure:[1.21.3] org.testcontainers:testcontainers:[2.0.2] From 552faad1900beb08e83726046f63d00004105cb4 Mon Sep 17 00:00:00 2001 From: muyao Date: Sat, 28 Feb 2026 11:09:16 +0800 Subject: [PATCH 11/22] fix --- sdk/spring/scripts/compatibility_delete_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/scripts/compatibility_delete_version.py b/sdk/spring/scripts/compatibility_delete_version.py index 7eac213e8e1e..b7b3b011cd4f 100644 --- a/sdk/spring/scripts/compatibility_delete_version.py +++ b/sdk/spring/scripts/compatibility_delete_version.py @@ -32,6 +32,7 @@ SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.mockito:mockito-core" }, "3.1.12": { + SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.apache.commons:commons-lang3", SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:azure", SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:testcontainers" }, @@ -41,7 +42,6 @@ SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:testcontainers" }, "3.3.13": { - SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.apache.commons:commons-lang3", SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:azure", SPRING_BOOT_MAJOR_3_VERSION_TAG_PREFIX + "org.testcontainers:testcontainers" } From 1f211639afd47d657f63a9e88198e281eec173b3 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Wed, 25 Feb 2026 09:14:43 +0800 Subject: [PATCH 12/22] Add changelog item about supporting EventHubs in docker compose and test container (#48082) * Add changelog item about supporting EventHubs in docker compose and test container * Fix error in issue number (cherry picked from commit 5dc47c7ebf574e12ecb54bedc79221d419a835ba) --- sdk/spring/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index 5635fbb667ad..bbd52e3ef6df 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -9,6 +9,7 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module. #### New Features - Add ConnectionDetails for ServiceBus. [#44019](https://github.com/Azure/azure-sdk-for-java/pull/44019). +- Add ConnectionDetails for EventHubs. [#47926](https://github.com/Azure/azure-sdk-for-java/pull/47926). ### Spring Cloud Azure Docker Compose @@ -17,6 +18,7 @@ This section includes changes in `spring-cloud-azure-docker-compose` module. #### New Features - Add ServiceBusDockerComposeConnectionDetailsFactory. [#44019](https://github.com/Azure/azure-sdk-for-java/pull/44019). +- Add EventHubsDockerComposeConnectionDetailsFactory. [#47926](https://github.com/Azure/azure-sdk-for-java/pull/47926). ### Spring Cloud Azure Test Containers @@ -25,6 +27,7 @@ This section includes changes in `spring-cloud-azure-testcontainers` module. #### New Features - Add ServiceBusContainerConnectionDetailsFactory. [#44019](https://github.com/Azure/azure-sdk-for-java/pull/44019). +- Add EventHubsContainerConnectionDetailsFactory. [#47926](https://github.com/Azure/azure-sdk-for-java/pull/47926). ## 5.24.1 (2025-12-09) - This release is compatible with Spring Boot 3.5.0-3.5.8, 3.4.0-3.4.12, 3.3.0-3.3.13, 3.2.0-3.2.12, 3.1.0-3.1.12, 3.0.0-3.0.13. (Note: 3.5.x (x>8) and 3.4.y (y>12) should be supported, but they aren't tested with this release.) From e6f1d163c1e3bc5412b938b57a83ef40f29ba55d Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Thu, 26 Feb 2026 17:21:57 +0800 Subject: [PATCH 13/22] Fix error about test by docker (#48105) (cherry picked from commit 8f82f8c9beb1510bd08b3f08c0f8a950325c0393) --- .../properties/ConfigurationWithConnectionDetailsBean.java | 1 + .../properties/ConfigurationWithConnectionDetailsBean.java | 1 + .../blob/properties/ConfigurationWithConnectionDetailsBean.java | 2 ++ .../properties/ConfigurationWithConnectionDetailsBean.java | 1 + 4 files changed, 5 insertions(+) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithConnectionDetailsBean.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithConnectionDetailsBean.java index 7eeea4a62f26..86c763571eef 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithConnectionDetailsBean.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/properties/ConfigurationWithConnectionDetailsBean.java @@ -40,6 +40,7 @@ AzureEventHubsProperties azureEventHubsProperties() { AzureEventHubsProperties properties = bindResult.isBound() ? bindResult.get() : propertiesLoadFromGlobalProperties; properties.setConnectionString(connectionDetails.getConnectionString()); + properties.setNamespace(null); return properties; } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithConnectionDetailsBean.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithConnectionDetailsBean.java index a52eb3684382..a3bd96e3d034 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithConnectionDetailsBean.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/ConfigurationWithConnectionDetailsBean.java @@ -40,6 +40,7 @@ AzureServiceBusProperties azureServiceBusProperties() { AzureServiceBusProperties properties = bindResult.isBound() ? bindResult.get() : propertiesLoadFromGlobalProperties; properties.setConnectionString(connectionDetails.getConnectionString()); + properties.setNamespace(null); return properties; } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/storage/blob/properties/ConfigurationWithConnectionDetailsBean.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/storage/blob/properties/ConfigurationWithConnectionDetailsBean.java index 9abbebf32c61..6ce1cfce4ccf 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/storage/blob/properties/ConfigurationWithConnectionDetailsBean.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/storage/blob/properties/ConfigurationWithConnectionDetailsBean.java @@ -38,6 +38,8 @@ AzureStorageBlobProperties azureStorageBlobProperties( AzureStorageBlobProperties properties = bindResult.isBound() ? bindResult.get() : propertiesLoadFromServiceCommonProperties; properties.setConnectionString(connectionDetails.getConnectionString()); + properties.setAccountName(null); + properties.setEndpoint(null); return properties; } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/storage/queue/properties/ConfigurationWithConnectionDetailsBean.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/storage/queue/properties/ConfigurationWithConnectionDetailsBean.java index 94aa271710ce..e0852aa363d7 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/storage/queue/properties/ConfigurationWithConnectionDetailsBean.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/storage/queue/properties/ConfigurationWithConnectionDetailsBean.java @@ -39,6 +39,7 @@ AzureStorageQueueProperties azureStorageQueueProperties( : propertiesLoadFromServiceCommonProperties; properties.setConnectionString(connectionDetails.getConnectionString()); properties.setEndpoint(connectionDetails.getEndpoint()); + properties.setAccountName(null); return properties; } From b996997a2bf7fcb8e9bbd19c805ce59586ba9057 Mon Sep 17 00:00:00 2001 From: Azure SDK Bot <53356347+azure-sdk@users.noreply.github.com> Date: Tue, 13 Jan 2026 16:32:07 -0800 Subject: [PATCH 14/22] Sync eng/common directory with azure-sdk-tools for PR 13555 (#47701) * usedotnet9 if no DOTNET_HOST is set * remove the UseDotnetVersion. set DOTNET_ROOT on linux/mac * small adjustment. linux doesn't work if we set this value --------- Co-authored-by: Scott Beddall (cherry picked from commit 65c01ea66c4c632f478377914214388528a29c92) --- eng/common/testproxy/test-proxy-standalone-tool.yml | 5 +++++ eng/common/testproxy/test-proxy-tool.yml | 3 +++ 2 files changed, 8 insertions(+) diff --git a/eng/common/testproxy/test-proxy-standalone-tool.yml b/eng/common/testproxy/test-proxy-standalone-tool.yml index fb9696e6de1f..1ced596eb648 100644 --- a/eng/common/testproxy/test-proxy-standalone-tool.yml +++ b/eng/common/testproxy/test-proxy-standalone-tool.yml @@ -74,6 +74,9 @@ steps: # nohup does NOT continue beyond the current session if you use it within powershell - bash: | + if [[ "$(uname)" == "Darwin" ]]; then + export DOTNET_ROOT="$HOME/.dotnet" + fi echo "nohup $(PROXY_EXE) 1>${{ parameters.rootFolder }}/test-proxy.log 2>${{ parameters.rootFolder }}/test-proxy-error.log &" nohup $(PROXY_EXE) 1>${{ parameters.rootFolder }}/test-proxy.log 2>${{ parameters.rootFolder }}/test-proxy-error.log & @@ -84,6 +87,8 @@ steps: displayName: "Run the testproxy - linux/mac" condition: and(succeeded(), ne(variables['Agent.OS'],'Windows_NT'), ${{ parameters.condition }}) workingDirectory: "${{ parameters.rootFolder }}" + env: + DOTNET_ROLL_FORWARD: 'Major' - pwsh: | for ($i = 0; $i -lt 10; $i++) { diff --git a/eng/common/testproxy/test-proxy-tool.yml b/eng/common/testproxy/test-proxy-tool.yml index 03c9dbaa00c1..f2d0fe6e30b0 100644 --- a/eng/common/testproxy/test-proxy-tool.yml +++ b/eng/common/testproxy/test-proxy-tool.yml @@ -87,6 +87,9 @@ steps: # nohup does NOT continue beyond the current session if you use it within powershell - bash: | + if [[ "$(uname)" == "Darwin" ]]; then + export DOTNET_ROOT="$HOME/.dotnet" + fi nohup $(Build.BinariesDirectory)/test-proxy/test-proxy 1>${{ parameters.rootFolder }}/test-proxy.log 2>${{ parameters.rootFolder }}/test-proxy-error.log & echo $! > $(Build.SourcesDirectory)/test-proxy.pid From 039ea849dfed0bb59a13254fb1e5514f886d26f1 Mon Sep 17 00:00:00 2001 From: Scott Beddall <45376673+scbedd@users.noreply.github.com> Date: Tue, 27 Jan 2026 19:28:11 -0800 Subject: [PATCH 15/22] handle non-blocking 1ESPT error (#47825) (cherry picked from commit a247fdd005d5c0aeee5878dba4ab06e997c18ab5) --- eng/pipelines/templates/stages/archetype-java-release-batch.yml | 2 +- eng/pipelines/templates/stages/archetype-java-release-patch.yml | 2 +- .../templates/stages/archetype-java-release-pom-only.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/pipelines/templates/stages/archetype-java-release-batch.yml b/eng/pipelines/templates/stages/archetype-java-release-batch.yml index 52ca68764cbf..a4367a280af6 100644 --- a/eng/pipelines/templates/stages/archetype-java-release-batch.yml +++ b/eng/pipelines/templates/stages/archetype-java-release-batch.yml @@ -92,7 +92,7 @@ stages: # the Validation step below publishes a package to a "burner" feed which is cleaned up after the # pipeline completes. - ${{if and(in(variables['Build.Reason'], 'Manual', ''), eq(variables['System.TeamProject'], 'internal'))}}: - - stage: + - stage: Release displayName: 'Releasing: ${{ length(parameters.Artifacts) }} libraries' dependsOn: Signing condition: and(succeeded(), ne(variables['SetDevVersion'], 'true'), ne(variables['Skip.Release'], 'true'), ne(variables['Build.Repository.Name'], 'Azure/azure-sdk-for-java-pr')) diff --git a/eng/pipelines/templates/stages/archetype-java-release-patch.yml b/eng/pipelines/templates/stages/archetype-java-release-patch.yml index eedda64b29c6..dc9becd0233d 100644 --- a/eng/pipelines/templates/stages/archetype-java-release-patch.yml +++ b/eng/pipelines/templates/stages/archetype-java-release-patch.yml @@ -80,7 +80,7 @@ stages: # the Validation step below publishes a package to a "burner" feed which is cleaned up after the # pipeline completes. - ${{if and(in(variables['Build.Reason'], 'Manual', ''), eq(variables['System.TeamProject'], 'internal'))}}: - - stage: + - stage: Release displayName: 'Releasing: ${{ length(parameters.Artifacts) }} libraries' dependsOn: Signing variables: diff --git a/eng/pipelines/templates/stages/archetype-java-release-pom-only.yml b/eng/pipelines/templates/stages/archetype-java-release-pom-only.yml index 244bb973bd0c..2f092fc9b72a 100644 --- a/eng/pipelines/templates/stages/archetype-java-release-pom-only.yml +++ b/eng/pipelines/templates/stages/archetype-java-release-pom-only.yml @@ -74,7 +74,7 @@ stages: # pipeline completes. - ${{if and(eq(variables['Build.Reason'], 'Manual'), eq(variables['System.TeamProject'], 'internal'))}}: - ${{ each artifact in parameters.Artifacts }}: - - stage: + - stage: Release_${{artifact.safeName}} displayName: 'Release: ${{artifact.name}}' dependsOn: Signing variables: From aff32716c64048d0bebc8e2dc92322e6b51b8e2e Mon Sep 17 00:00:00 2001 From: Ray Chen Date: Thu, 4 Sep 2025 16:26:40 -0700 Subject: [PATCH 16/22] Generic approach to install java 8 on macOS (#46593) * Added separte step to install java8 * Install java 8 in the install-latest-jdk template and set java_home * Check null for the Java_Home * Cleaned up code (cherry picked from commit 625b333012433dd72d41403d3a1cbd742f35bf5b) --- .../templates/steps/install-latest-jdk.yml | 10 ++++++ eng/scripts/Install-Latest-JDK.ps1 | 32 ++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/eng/pipelines/templates/steps/install-latest-jdk.yml b/eng/pipelines/templates/steps/install-latest-jdk.yml index 8149c86e16d1..3c7273d81938 100644 --- a/eng/pipelines/templates/steps/install-latest-jdk.yml +++ b/eng/pipelines/templates/steps/install-latest-jdk.yml @@ -31,3 +31,13 @@ steps: Write-Host "Latest JDK: $Env:JAVA_HOME_${{ parameters.LatestJdkFeatureVersion }}_X64" displayName: 'Verify Latest JDK Install' condition: eq(variables['IsLatestNonLtsJdk'], 'true') + + - task: PowerShell@2 + displayName: 'Install JDK 8 on macOS' + inputs: + pwsh: true + arguments: > + -JdkFeatureVersion 8 + workingDirectory: $(Agent.BuildDirectory) + filePath: eng/scripts/Install-Latest-JDK.ps1 + condition: eq(variables['Agent.OS'], 'Darwin') diff --git a/eng/scripts/Install-Latest-JDK.ps1 b/eng/scripts/Install-Latest-JDK.ps1 index c5ae818508b8..4a6c472a7285 100644 --- a/eng/scripts/Install-Latest-JDK.ps1 +++ b/eng/scripts/Install-Latest-JDK.ps1 @@ -16,6 +16,19 @@ if ($IsWindows) { $os = "linux" } +$jdkFeatureVersionJavaHome = "JAVA_HOME_" + $JdkFeatureVersion + "_X64" +Write-Host "Checking if $jdkFeatureVersionJavaHome is already set and exists..." +$javaHomeValue = [System.Environment]::GetEnvironmentVariable($jdkFeatureVersionJavaHome) +if ($javaHomeValue) { + $jdkBinPath = Join-Path -Path $javaHomeValue -ChildPath "bin/java" + if (Test-Path -Path $jdkBinPath) { + Write-Host "$jdkFeatureVersionJavaHome is already set to $javaHomeValue" + exit 0 + } +} else { + Write-Host "$jdkFeatureVersionJavaHome is not set, proceeding with installation..." +} + $getInstalls = "$adoptiumApiUrl/v3/assets/latest/$JdkFeatureVersion/hotspot?architecture=x64&image_type=jdk&os=$os&vendor=eclipse" $jdkUnzipName = "jdk-$JdkFeatureVersion" @@ -43,11 +56,22 @@ if (!(Test-Path -Path $jdkUnzipName -PathType container)) { } $javaHome = (Convert-Path $jdkUnzipName) -Write-Host "Latest JDK: $javaHome" +if ($IsMacOS) { + # On macOS, the JDK is inside a subdirectory of the unzipped folder. + $correctJavaHome = Join-Path -Path $javaHome -ChildPath "Contents/Home" + $javaBinPath = Join-Path -Path $correctJavaHome -ChildPath "bin/java" + if (Test-Path $javaBinPath) { + $javaHome = $correctJavaHome + Write-Host "Updated JAVA_HOME on macOS: $correctJavaHome" + } else { + Write-Error "Failed to find Java at: $correctJavaHome" + exit 1 + } +} + +Write-Host "Latest JDK: $javaHome" Write-Host "Current JAVA_HOME: $Env:JAVA_HOME" Write-Host "##vso[task.setvariable variable=JAVA_HOME;]$javaHome" -Write-Host "Updated JAVA_HOME: $Env:JAVA_HOME" - -$jdkFeatureVersionJavaHome = "JAVA_HOME_" + $JdkFeatureVersion + "_X64" +Write-Host "Updated JAVA_HOME to : $javaHome" Write-Host "##vso[task.setvariable variable=$jdkFeatureVersionJavaHome;]$javaHome" From 43a306a099281f1c2327a9d8479abe5a6132a0d8 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Fri, 26 Dec 2025 00:59:55 +0000 Subject: [PATCH 17/22] Fix error reported by 'Link verification check' (#47603) (cherry picked from commit cb363c719c1747c2247131ff056d6ebd892468be) --- sdk/spring/azure-spring-data-cosmos/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/spring/azure-spring-data-cosmos/README.md b/sdk/spring/azure-spring-data-cosmos/README.md index 216b5aaf41f3..ae77066607ea 100644 --- a/sdk/spring/azure-spring-data-cosmos/README.md +++ b/sdk/spring/azure-spring-data-cosmos/README.md @@ -280,8 +280,8 @@ java -javaagent:"" -jar #### Using database provisioned throughput -Cosmos supports both [container](https://learn.microsoft.com/azure/cosmos-db/sql/how-to-provision-container-throughput) -and [database](https://learn.microsoft.com/azure/cosmos-db/sql/how-to-provision-database-throughput) provisioned +Cosmos supports both [container](https://learn.microsoft.com/azure/cosmos-db/how-to-provision-container-throughput) +and [database](https://learn.microsoft.com/azure/cosmos-db/how-to-provision-database-throughput) provisioned throughput. By default, spring-data-cosmos will provision throughput for each container created. If you prefer to share throughput between containers, you can enable database provisioned throughput via CosmosConfig. @@ -608,7 +608,7 @@ public class MyItem { String _etag; } ``` -- Read more about Optimistic Locking [here](https://learn.microsoft.com/azure/cosmos-db/sql/database-transactions-optimistic-concurrency#optimistic-concurrency-control) +- Read more about Optimistic Locking [here](https://learn.microsoft.com/azure/cosmos-db/database-transactions-optimistic-concurrency#optimistic-concurrency-control) ### Spring Data custom query, pageable and sorting - Azure-spring-data-cosmos supports [spring data custom queries][spring_data_custom_query] From 7bc53be54c15ed186acdc4e513c8c57809136524 Mon Sep 17 00:00:00 2001 From: muyao Date: Sat, 28 Feb 2026 15:00:08 +0800 Subject: [PATCH 18/22] fix --- sdk/spring/README.md | 2 +- sdk/spring/azure-spring-data-cosmos/README.md | 4 ++-- .../README.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/spring/README.md b/sdk/spring/README.md index ff739590349a..6809b4801868 100644 --- a/sdk/spring/README.md +++ b/sdk/spring/README.md @@ -189,7 +189,7 @@ dependencyManagement { ## Spring Boot 3 Support -The source code of Spring Cloud Azure for Spring Boot 3.x can be found on the [feature/spring-boot-3](https://github.com/Azure/azure-sdk-for-java/tree/feature/spring-boot-3) branch. +Spring Cloud Azure 5.25.0 is the latest supported version of Spring Boot 3.x. #### Spring AOT and Spring native images diff --git a/sdk/spring/azure-spring-data-cosmos/README.md b/sdk/spring/azure-spring-data-cosmos/README.md index ae77066607ea..0ff09140e7d2 100644 --- a/sdk/spring/azure-spring-data-cosmos/README.md +++ b/sdk/spring/azure-spring-data-cosmos/README.md @@ -1148,8 +1148,8 @@ or contact [opencode@microsoft.com][coc_contact] with any additional questions o [coc_contact]: mailto:opencode@microsoft.com [azure_subscription]: https://azure.microsoft.com/free/ [samples]: https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos -[sample-for-multi-database]: https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-cloud-azure_v4.3.0/cosmos/azure-spring-data-cosmos/cosmos-multi-database-multi-account -[sample-for-multi-database-single-account]: https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-cloud-azure_v4.3.0/cosmos/azure-spring-data-cosmos/cosmos-multi-database-single-account +[sample-for-multi-database]: https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-boot-3.x/cosmos/azure-spring-data-cosmos/cosmos-multi-database-multi-account +[sample-for-multi-database-single-account]: https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-boot-3.x/cosmos/azure-spring-data-cosmos/cosmos-multi-database-single-account [sql_api_query]: https://learn.microsoft.com/azure/cosmos-db/sql-api-sql-query [local_emulator]: https://learn.microsoft.com/azure/cosmos-db/local-emulator [local_emulator_export_ssl_certificates]: https://learn.microsoft.com/azure/cosmos-db/local-emulator-export-ssl-certificates diff --git a/sdk/spring/spring-cloud-azure-starter-appconfiguration-config/README.md b/sdk/spring/spring-cloud-azure-starter-appconfiguration-config/README.md index b23adc9fce07..568483f89e7b 100644 --- a/sdk/spring/spring-cloud-azure-starter-appconfiguration-config/README.md +++ b/sdk/spring/spring-cloud-azure-starter-appconfiguration-config/README.md @@ -404,9 +404,9 @@ Please follow [instructions here][contributing_md] to build from source or contr [package]: https://mvnrepository.com/artifact/com.microsoft.azure/spring-cloud-azure-appconfiguration-config -[app_configuration_sample]: https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-cloud-azure_v4.3.0/appconfiguration/azure-spring-cloud-appconfiguration-config/azure-spring-cloud-appconfiguration-config-sample -[app_configuration_conversation_complete_sample]: https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-cloud-azure_v4.3.0/appconfiguration/azure-spring-cloud-appconfiguration-config/azure-spring-cloud-appconfiguration-config-convert-sample/azure-spring-cloud-appconfiguration-config-convert-sample-complete -[app_configuration_conversation_initail_sample]: https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-cloud-azure_v4.3.0/appconfiguration/azure-spring-cloud-appconfiguration-config/azure-spring-cloud-appconfiguration-config-convert-sample/azure-spring-cloud-appconfiguration-config-convert-sample-initial +[app_configuration_sample]: https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-boot-3.x/appconfiguration/spring-cloud-azure-appconfiguration-config/spring-cloud-azure-appconfiguration-config-sample +[app_configuration_conversation_complete_sample]: https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-boot-3.x/appconfiguration/spring-cloud-azure-appconfiguration-config/spring-cloud-azure-appconfiguration-config-convert-sample/spring-cloud-azure-appconfiguration-config-convert-sample-complete +[app_configuration_conversation_initail_sample]: https://github.com/Azure-Samples/azure-spring-boot-samples/tree/spring-boot-3.x/appconfiguration/spring-cloud-azure-appconfiguration-config/spring-cloud-azure-appconfiguration-config-convert-sample/spring-cloud-azure-appconfiguration-config-convert-sample-initial [azure_subscription]: https://azure.microsoft.com/free [spring logging document]: https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#boot-features-logging [contributing_md]: https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/spring/CONTRIBUTING.md From 2a39cf532e5bc9c625215fc1d3c3ff891029daba Mon Sep 17 00:00:00 2001 From: muyao Date: Mon, 2 Mar 2026 09:30:11 +0800 Subject: [PATCH 19/22] remove Spring Boot 3.0 tests --- sdk/spring/pipeline/spring-cloud-azure-supported-spring.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/pipeline/spring-cloud-azure-supported-spring.json b/sdk/spring/pipeline/spring-cloud-azure-supported-spring.json index ea26b6a99dde..0156d7f2a5e6 100644 --- a/sdk/spring/pipeline/spring-cloud-azure-supported-spring.json +++ b/sdk/spring/pipeline/spring-cloud-azure-supported-spring.json @@ -563,7 +563,7 @@ "current" : false, "releaseStatus" : "GENERAL_AVAILABILITY", "snapshot" : false, - "supportStatus" : "SUPPORTED", + "supportStatus" : "END_OF_LIFE", "spring-boot-version" : "3.0.13", "spring-cloud-version" : "2022.0.5" }, From 4ee605caba1dade5cc2839a1f61a54d68e9812f7 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Mon, 2 Mar 2026 14:45:45 +0800 Subject: [PATCH 20/22] Update dependency in spring-cloud-azure-testcontainers (#48178) (cherry picked from commit e348586d127eb4a0f97f91a7261cb76a38d16b27) --- eng/versioning/external_dependencies.txt | 4 ++-- sdk/spring/spring-cloud-azure-testcontainers/pom.xml | 11 +++++------ ...viceBusContainerConnectionDetailsFactoryTests.java | 1 + 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index 46b112e9bbd6..f1ea1933cdc0 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -411,8 +411,8 @@ springboot3_org.springframework:spring-test;6.2.9 springboot3_org.springframework:spring-tx;6.2.9 springboot3_org.springframework:spring-web;6.2.9 springboot3_org.springframework:spring-webmvc;6.2.9 -springboot3_org.testcontainers:junit-jupiter;1.21.3 -springboot3_org.testcontainers:azure;1.21.3 +springboot3_org.testcontainers:testcontainers-junit-jupiter;2.0.3 +springboot3_org.testcontainers:testcontainers-azure;2.0.3 springboot3_org.testcontainers:testcontainers;2.0.2 springboot3_org.awaitility:awaitility;4.3.0 springboot3_com.microsoft.sqlserver:mssql-jdbc;13.2.1.jre11 diff --git a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml index 472ab6e49c68..429f633914e1 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml +++ b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml @@ -102,9 +102,8 @@ org.testcontainers - azure - 1.21.3 - true + testcontainers-azure + 2.0.3 + testcontainers-junit-jupiter + 2.0.3 test @@ -209,7 +208,7 @@ org.apache.commons:commons-lang3:[3.17.0] org.springframework.boot:spring-boot-testcontainers:[3.5.4] - org.testcontainers:azure:[1.21.3] + org.testcontainers:azure:[2.0.3] org.testcontainers:testcontainers:[2.0.2] com.microsoft.sqlserver:mssql-jdbc:[13.2.1.jre11] diff --git a/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java b/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java index de67d4779f35..c77ce4c646eb 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java +++ b/sdk/spring/spring-cloud-azure-testcontainers/src/test/java/com/azure/spring/cloud/testcontainers/implementation/service/connection/bus/ServiceBusContainerConnectionDetailsFactoryTests.java @@ -42,6 +42,7 @@ "spring.cloud.azure.servicebus.entity-type=queue" }) @Testcontainers @EnabledOnOs(OS.LINUX) +@SuppressWarnings("deprecation") // Link to related issue: https://github.com/testcontainers/testcontainers-java/issues/11554 class ServiceBusContainerConnectionDetailsFactoryTests { private static final Network NETWORK = Network.newNetwork(); From be59e1a70af309a2a45a30683aa9cbd677db10fa Mon Sep 17 00:00:00 2001 From: muyao Date: Mon, 2 Mar 2026 14:57:27 +0800 Subject: [PATCH 21/22] update --- eng/versioning/external_dependencies.txt | 2 +- sdk/spring/spring-cloud-azure-testcontainers/pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index f1ea1933cdc0..3296b24c5f4f 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -413,7 +413,7 @@ springboot3_org.springframework:spring-web;6.2.9 springboot3_org.springframework:spring-webmvc;6.2.9 springboot3_org.testcontainers:testcontainers-junit-jupiter;2.0.3 springboot3_org.testcontainers:testcontainers-azure;2.0.3 -springboot3_org.testcontainers:testcontainers;2.0.2 +springboot3_org.testcontainers:testcontainers;2.0.3 springboot3_org.awaitility:awaitility;4.3.0 springboot3_com.microsoft.sqlserver:mssql-jdbc;13.2.1.jre11 # Used for Spring version updates diff --git a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml index 429f633914e1..bf3a8c6d07d4 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml +++ b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml @@ -98,7 +98,7 @@ org.testcontainers testcontainers - 2.0.2 + 2.0.3 org.testcontainers @@ -209,7 +209,7 @@ org.apache.commons:commons-lang3:[3.17.0] org.springframework.boot:spring-boot-testcontainers:[3.5.4] org.testcontainers:azure:[2.0.3] - org.testcontainers:testcontainers:[2.0.2] + org.testcontainers:testcontainers:[2.0.3] com.microsoft.sqlserver:mssql-jdbc:[13.2.1.jre11] From 1e7a8ca5887d896f0b84aa4379aec0840014ec3f Mon Sep 17 00:00:00 2001 From: muyao Date: Mon, 2 Mar 2026 15:14:42 +0800 Subject: [PATCH 22/22] fix --- sdk/spring/spring-cloud-azure-testcontainers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml index bf3a8c6d07d4..9b078e99a396 100644 --- a/sdk/spring/spring-cloud-azure-testcontainers/pom.xml +++ b/sdk/spring/spring-cloud-azure-testcontainers/pom.xml @@ -208,7 +208,7 @@ org.apache.commons:commons-lang3:[3.17.0] org.springframework.boot:spring-boot-testcontainers:[3.5.4] - org.testcontainers:azure:[2.0.3] + org.testcontainers:testcontainers-azure:[2.0.3] org.testcontainers:testcontainers:[2.0.3] com.microsoft.sqlserver:mssql-jdbc:[13.2.1.jre11]