Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
Expand Down Expand Up @@ -88,12 +89,14 @@ static class ProcessorContainerConfiguration {
@ConditionalOnMissingBean
EventHubsProcessorFactory defaultEventHubsNamespaceProcessorFactory(
NamespaceProperties properties,
ApplicationContext applicationContext,
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.setApplicationContext(applicationContext);
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
return factory;
Expand All @@ -108,10 +111,12 @@ static class EventHubsTemplateConfiguration {
@ConditionalOnMissingBean
EventHubsProducerFactory defaultEventHubsNamespaceProducerFactory(
NamespaceProperties properties,
ApplicationContext applicationContext,
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.setApplicationContext(applicationContext);
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
return factory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
Expand Down Expand Up @@ -92,13 +93,15 @@ static class ProcessorContainerConfiguration {
@ConditionalOnMissingBean
ServiceBusProcessorFactory defaultServiceBusNamespaceProcessorFactory(
NamespaceProperties properties,
ApplicationContext applicationContext,
ObjectProvider<PropertiesSupplier<ConsumerIdentifier, ProcessorProperties>> suppliers,
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials,
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder>> clientBuilderCustomizers,
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder.ServiceBusProcessorClientBuilder>> processorClientBuilderCustomizers,
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder.ServiceBusSessionProcessorClientBuilder>> sessionProcessorClientBuilderCustomizers) {
DefaultServiceBusNamespaceProcessorFactory factory = new DefaultServiceBusNamespaceProcessorFactory(properties, suppliers.getIfAvailable());
factory.setApplicationContext(applicationContext);
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
clientBuilderCustomizers.orderedStream().forEach(factory::addServiceBusClientBuilderCustomizer);
Expand All @@ -115,12 +118,14 @@ static class ConsumerContainerConfiguration {
@ConditionalOnMissingBean
ServiceBusConsumerFactory defaultServiceBusNamespaceConsumerFactory(
NamespaceProperties properties,
ApplicationContext applicationContext,
ObjectProvider<PropertiesSupplier<ConsumerIdentifier, ConsumerProperties>> suppliers,
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
@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());
factory.setApplicationContext(applicationContext);
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
customizers.orderedStream().forEach(factory::addServiceBusClientBuilderCustomizer);
Expand All @@ -136,12 +141,14 @@ static class ServiceBusTemplateConfiguration {
@ConditionalOnMissingBean
ServiceBusProducerFactory defaultServiceBusNamespaceProducerFactory(
NamespaceProperties properties,
ApplicationContext applicationContext,
ObjectProvider<PropertiesSupplier<String, ProducerProperties>> suppliers,
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
@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());
factory.setApplicationContext(applicationContext);
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
clientBuilderCustomizers.orderedStream().forEach(factory::addServiceBusClientBuilderCustomizer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import com.azure.core.credential.TokenCredential;
import com.azure.messaging.eventhubs.CheckpointStore;
import com.azure.messaging.eventhubs.EventHubProducerAsyncClient;
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;
Expand Down Expand Up @@ -148,48 +149,43 @@ void testCustomTokenCredentialConfiguration() {
AzureTokenCredentialAutoConfiguration.class,
AzureGlobalPropertiesAutoConfiguration.class))
.withBean(EventHubsMessageConverter.class, EventHubsMessageConverter::new)
.withBean(CheckpointStore.class, TestCheckpointStore::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
TokenCredential customCredential = context.getBean("customTokenCredential", TokenCredential.class);
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")
EventHubsProcessorFactory processorFactory = context.getBean(EventHubsProcessorFactory.class);

// Validate credential resolution - without ApplicationContext propagation fix,
// tokenCredentialBeanName would be silently ignored and connection string would be used
assertThat(resolveCredential(producerFactory, eventHubsProperties))
.isSameAs(customCredential);
assertThat(resolveCredential(processorFactory, eventHubsProperties))
.isSameAs(customCredential);

// Validate runtime producer creation
EventHubProducerAsyncClient producer = producerFactory.createProducer("test-eventhub");
producer.close();
});
}

@SuppressWarnings("unchecked")
private TokenCredential resolveCredential(Object factory, AzureEventHubsProperties properties) throws Exception {
Field field = factory.getClass().getDeclaredField("tokenCredentialResolver");
field.setAccessible(true);
AzureCredentialResolver<TokenCredential> resolver = (AzureCredentialResolver<TokenCredential>) field.get(factory);
return resolver.resolve(properties);
}

@Configuration
public static class CustomTokenCredentialConfiguration {
@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration;
import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties;
import com.azure.spring.cloud.core.credential.AzureCredentialResolver;
import com.azure.spring.cloud.service.servicebus.properties.ServiceBusEntityType;
import com.azure.spring.messaging.servicebus.core.ServiceBusConsumerFactory;
import com.azure.spring.messaging.servicebus.core.ServiceBusProcessorFactory;
import com.azure.spring.messaging.servicebus.core.ServiceBusProducerFactory;
import com.azure.spring.messaging.servicebus.core.ServiceBusTemplate;
Expand Down Expand Up @@ -150,44 +152,36 @@ void testCustomTokenCredentialConfiguration() {
)
.withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class)
.run(context -> {

// Verify that the properties contain the correct credential bean name
TokenCredential customCredential = context.getBean("customTokenCredential", TokenCredential.class);
AzureServiceBusProperties serviceBusProperties = context.getBean(AzureServiceBusProperties.class);
assertThat(serviceBusProperties).isNotNull();
assertThat(serviceBusProperties.getCredential()).isNotNull();

assertThat(serviceBusProperties.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 ServiceBusProducerFactory has the tokenCredentialResolver configured
assertThat(context).hasSingleBean(ServiceBusProducerFactory.class);
ServiceBusProducerFactory producerFactory = context.getBean(ServiceBusProducerFactory.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(serviceBusProperties);
assertThat(resolvedCredential)
.as("The resolved credential should be the customTokenCredential bean")
ServiceBusProcessorFactory processorFactory = context.getBean(ServiceBusProcessorFactory.class);
ServiceBusConsumerFactory consumerFactory = context.getBean(ServiceBusConsumerFactory.class);

assertThat(resolveCredential(producerFactory, serviceBusProperties))
.isSameAs(customCredential);
assertThat(resolveCredential(processorFactory, serviceBusProperties))
.isSameAs(customCredential);
assertThat(resolveCredential(consumerFactory, serviceBusProperties))
.isSameAs(customCredential);

// Validate runtime producer creation
producerFactory.createProducer("test-queue", ServiceBusEntityType.QUEUE).close();
});
}

@SuppressWarnings("unchecked")
private TokenCredential resolveCredential(Object factory, AzureServiceBusProperties properties) throws Exception {
Field field = factory.getClass().getDeclaredField("tokenCredentialResolver");
field.setAccessible(true);
AzureCredentialResolver<TokenCredential> resolver = (AzureCredentialResolver<TokenCredential>) field.get(factory);
return resolver.resolve(properties);
}

@Configuration
public static class CustomTokenCredentialConfiguration {
@Bean
Expand Down
Loading
Loading