Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions sdk/spring/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Release History

## 6.1.0 (Not released)

### Spring Cloud Azure Autoconfigure
This section includes changes in `spring-cloud-azure-autoconfigure` module.

#### Bugs Fixed

- 2 `TokenCredential` bean found in AzureServiceBusMessagingAutoConfiguration. [#47470](https://github.com/Azure/azure-sdk-for-java/pull/47470)
- `spring.cloud.azure.eventhubs.credential.token-credential-bean-name` not take effect in AzureEventHubsMessagingAutoConfiguration. [#47470](https://github.com/Azure/azure-sdk-for-java/pull/47470)

## 6.0.0 (2025-09-22)
- This release is compatible with Spring Boot 3.5.0-3.5.5. (Note: 3.5.x (x>5) should be supported, but they aren't tested with this release.)
- This release is compatible with Spring Cloud 2025.0.0. (Note: 2025.0.x(x>0) should be supported, but they aren't tested with this release.)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

package com.azure.spring.cloud.autoconfigure.implementation.eventhubs;

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;
import com.azure.spring.cloud.core.service.AzureServiceType;
import com.azure.spring.messaging.ConsumerIdentifier;
Expand All @@ -27,6 +29,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
Expand All @@ -37,6 +40,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import static com.azure.spring.cloud.autoconfigure.implementation.context.AzureContextUtils.DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME;
import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyAzureCommonProperties;

/**
Expand Down Expand Up @@ -83,10 +87,16 @@ static class ProcessorContainerConfiguration {
@Bean
@ConditionalOnMissingBean
EventHubsProcessorFactory defaultEventHubsNamespaceProcessorFactory(
NamespaceProperties properties, CheckpointStore checkpointStore,
ObjectProvider<PropertiesSupplier<ConsumerIdentifier, ProcessorProperties>> suppliers) {
return new DefaultEventHubsNamespaceProcessorFactory(checkpointStore, properties,
NamespaceProperties properties,
CheckpointStore checkpointStore,
ObjectProvider<PropertiesSupplier<ConsumerIdentifier, ProcessorProperties>> suppliers,
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials) {
DefaultEventHubsNamespaceProcessorFactory factory = new DefaultEventHubsNamespaceProcessorFactory(checkpointStore, properties,
suppliers.getIfAvailable());
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
return factory;
}

}
Expand All @@ -98,8 +108,13 @@ static class EventHubsTemplateConfiguration {
@ConditionalOnMissingBean
EventHubsProducerFactory defaultEventHubsNamespaceProducerFactory(
NamespaceProperties properties,
ObjectProvider<PropertiesSupplier<String, ProducerProperties>> suppliers) {
return new DefaultEventHubsNamespaceProducerFactory(properties, suppliers.getIfAvailable());
ObjectProvider<PropertiesSupplier<String, ProducerProperties>> suppliers,
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials) {
DefaultEventHubsNamespaceProducerFactory factory = new DefaultEventHubsNamespaceProducerFactory(properties, suppliers.getIfAvailable());
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
return factory;
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
Expand All @@ -44,6 +45,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import static com.azure.spring.cloud.autoconfigure.implementation.context.AzureContextUtils.DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME;
import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyAzureCommonProperties;


Expand Down Expand Up @@ -92,7 +94,7 @@ ServiceBusProcessorFactory defaultServiceBusNamespaceProcessorFactory(
NamespaceProperties properties,
ObjectProvider<PropertiesSupplier<ConsumerIdentifier, ProcessorProperties>> suppliers,
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
ObjectProvider<TokenCredential> defaultTokenCredentials,
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials,
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder>> clientBuilderCustomizers,
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder.ServiceBusProcessorClientBuilder>> processorClientBuilderCustomizers,
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder.ServiceBusSessionProcessorClientBuilder>> sessionProcessorClientBuilderCustomizers) {
Expand All @@ -115,7 +117,7 @@ ServiceBusConsumerFactory defaultServiceBusNamespaceConsumerFactory(
NamespaceProperties properties,
ObjectProvider<PropertiesSupplier<ConsumerIdentifier, ConsumerProperties>> suppliers,
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
ObjectProvider<TokenCredential> defaultTokenCredentials,
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials,
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder>> customizers,
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder.ServiceBusSessionReceiverClientBuilder>> sessionReceiverCustomizers) {
DefaultServiceBusNamespaceConsumerFactory factory = new DefaultServiceBusNamespaceConsumerFactory(properties, suppliers.getIfAvailable());
Expand All @@ -136,7 +138,7 @@ ServiceBusProducerFactory defaultServiceBusNamespaceProducerFactory(
NamespaceProperties properties,
ObjectProvider<PropertiesSupplier<String, ProducerProperties>> suppliers,
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
ObjectProvider<TokenCredential> defaultTokenCredentials,
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials,
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder>> clientBuilderCustomizers,
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder.ServiceBusSenderClientBuilder>> senderClientBuilderCustomizers) {
DefaultServiceBusNamespaceProducerFactory factory = new DefaultServiceBusNamespaceProducerFactory(properties, suppliers.getIfAvailable());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@

package com.azure.spring.cloud.autoconfigure.implementation.eventhubs;

import com.azure.core.credential.TokenCredential;
import com.azure.messaging.eventhubs.CheckpointStore;
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.eventhubs.configuration.TestCheckpointStore;
import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsProperties;
import com.azure.spring.cloud.core.credential.AzureCredentialResolver;
import com.azure.spring.messaging.eventhubs.core.EventHubsProcessorFactory;
import com.azure.spring.messaging.eventhubs.core.EventHubsProducerFactory;
import com.azure.spring.messaging.eventhubs.core.EventHubsTemplate;
import com.azure.spring.messaging.eventhubs.implementation.support.converter.EventHubsMessageConverter;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -14,10 +20,15 @@
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Field;

import static com.azure.spring.cloud.autoconfigure.implementation.eventhubs.EventHubsTestUtils.CONNECTION_STRING_FORMAT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.Mockito.mock;

class AzureEventHubsMessagingAutoConfigurationTests {

Expand Down Expand Up @@ -121,7 +132,7 @@ void withUserProvidedObjectMapper() {
.withPropertyValues("spring.cloud.azure.eventhubs.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace"),
"spring.cloud.azure.message-converter.isolated-object-mapper=false")
.withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class)
.withBean("userObjectMapper", ObjectMapper.class, () -> new ObjectMapper())
.withBean("userObjectMapper", ObjectMapper.class, ObjectMapper::new)
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class))
.run(context -> {
assertThat(context).hasBean("userObjectMapper");
Expand All @@ -130,4 +141,61 @@ void withUserProvidedObjectMapper() {
});
}

@Test
void testCustomTokenCredentialConfiguration() {
this.contextRunner
.withConfiguration(AutoConfigurations.of(CustomTokenCredentialConfiguration.class,
AzureTokenCredentialAutoConfiguration.class,
AzureGlobalPropertiesAutoConfiguration.class))
.withBean(EventHubsMessageConverter.class, EventHubsMessageConverter::new)
.withPropertyValues(
"spring.cloud.azure.eventhubs.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace"),
"spring.cloud.azure.eventhubs.credential.token-credential-bean-name=customTokenCredential"
)
.withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class)
.run(context -> {

// Verify that the properties contain the correct credential bean name
AzureEventHubsProperties eventHubsProperties = context.getBean(AzureEventHubsProperties.class);
assertThat(eventHubsProperties).isNotNull();
assertThat(eventHubsProperties.getCredential()).isNotNull();
assertThat(eventHubsProperties.getCredential().getTokenCredentialBeanName())
.as("The token-credential-bean-name property should be set to customTokenCredential")
.isEqualTo("customTokenCredential");

// Verify that the custom token credential bean exists
assertThat(context).hasBean("customTokenCredential");
TokenCredential customCredential = context.getBean("customTokenCredential", TokenCredential.class);
assertThat(customCredential).isNotNull();

// Verify the EventHubsProducerFactory has the tokenCredentialResolver configured
assertThat(context).hasSingleBean(EventHubsProducerFactory.class);
EventHubsProducerFactory producerFactory = context.getBean(EventHubsProducerFactory.class);
assertThat(producerFactory).isNotNull();

// Verify tokenCredentialResolver resolves to the custom credential
Field tokenCredentialResolverField = producerFactory.getClass().getDeclaredField("tokenCredentialResolver");
tokenCredentialResolverField.setAccessible(true);
Object tokenCredentialResolver = tokenCredentialResolverField.get(producerFactory);
assertThat(tokenCredentialResolver).as("TokenCredentialResolver should be configured").isNotNull();

// Cast to AzureCredentialResolver and invoke resolve() to verify it returns customTokenCredential
@SuppressWarnings("unchecked")
AzureCredentialResolver<TokenCredential> resolver =
(AzureCredentialResolver<TokenCredential>) tokenCredentialResolver;
TokenCredential resolvedCredential = resolver.resolve(eventHubsProperties);
assertThat(resolvedCredential)
.as("The resolved credential should be the customTokenCredential bean")
.isSameAs(customCredential);
});
}

@Configuration
public static class CustomTokenCredentialConfiguration {
@Bean
public TokenCredential customTokenCredential() {
return mock(TokenCredential.class);
}
}

}
Loading
Loading