Skip to content

Commit 8af6274

Browse files
authored
Fix bug: 'token-credential-bean-name' not work in spring-cloud-azure-stream-binder (#47557)
* Add unit test to confirm 'token-credential-bean-name' property work in spring-cloud-azure-stream-binder * Fix bug: 'token-credential-bean-name' not work in spring-cloud-azure-stream-binder * Fix unit test failure * Use another method to fix the test failure
1 parent 36b5a7d commit 8af6274

File tree

20 files changed

+375
-95
lines changed

20 files changed

+375
-95
lines changed

sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsMessagingAutoConfiguration.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3737
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3838
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
39+
import org.springframework.context.ApplicationContext;
3940
import org.springframework.context.annotation.Bean;
4041
import org.springframework.context.annotation.Configuration;
4142
import org.springframework.context.annotation.Import;
@@ -88,12 +89,14 @@ static class ProcessorContainerConfiguration {
8889
@ConditionalOnMissingBean
8990
EventHubsProcessorFactory defaultEventHubsNamespaceProcessorFactory(
9091
NamespaceProperties properties,
92+
ApplicationContext applicationContext,
9193
CheckpointStore checkpointStore,
9294
ObjectProvider<PropertiesSupplier<ConsumerIdentifier, ProcessorProperties>> suppliers,
9395
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
9496
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials) {
9597
DefaultEventHubsNamespaceProcessorFactory factory = new DefaultEventHubsNamespaceProcessorFactory(checkpointStore, properties,
9698
suppliers.getIfAvailable());
99+
factory.setApplicationContext(applicationContext);
97100
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
98101
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
99102
return factory;
@@ -108,10 +111,12 @@ static class EventHubsTemplateConfiguration {
108111
@ConditionalOnMissingBean
109112
EventHubsProducerFactory defaultEventHubsNamespaceProducerFactory(
110113
NamespaceProperties properties,
114+
ApplicationContext applicationContext,
111115
ObjectProvider<PropertiesSupplier<String, ProducerProperties>> suppliers,
112116
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
113117
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials) {
114118
DefaultEventHubsNamespaceProducerFactory factory = new DefaultEventHubsNamespaceProducerFactory(properties, suppliers.getIfAvailable());
119+
factory.setApplicationContext(applicationContext);
115120
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
116121
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
117122
return factory;

sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfiguration.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
4242
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
4343
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
44+
import org.springframework.context.ApplicationContext;
4445
import org.springframework.context.annotation.Bean;
4546
import org.springframework.context.annotation.Configuration;
4647
import org.springframework.context.annotation.Import;
@@ -92,13 +93,15 @@ static class ProcessorContainerConfiguration {
9293
@ConditionalOnMissingBean
9394
ServiceBusProcessorFactory defaultServiceBusNamespaceProcessorFactory(
9495
NamespaceProperties properties,
96+
ApplicationContext applicationContext,
9597
ObjectProvider<PropertiesSupplier<ConsumerIdentifier, ProcessorProperties>> suppliers,
9698
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
9799
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials,
98100
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder>> clientBuilderCustomizers,
99101
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder.ServiceBusProcessorClientBuilder>> processorClientBuilderCustomizers,
100102
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder.ServiceBusSessionProcessorClientBuilder>> sessionProcessorClientBuilderCustomizers) {
101103
DefaultServiceBusNamespaceProcessorFactory factory = new DefaultServiceBusNamespaceProcessorFactory(properties, suppliers.getIfAvailable());
104+
factory.setApplicationContext(applicationContext);
102105
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
103106
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
104107
clientBuilderCustomizers.orderedStream().forEach(factory::addServiceBusClientBuilderCustomizer);
@@ -115,12 +118,14 @@ static class ConsumerContainerConfiguration {
115118
@ConditionalOnMissingBean
116119
ServiceBusConsumerFactory defaultServiceBusNamespaceConsumerFactory(
117120
NamespaceProperties properties,
121+
ApplicationContext applicationContext,
118122
ObjectProvider<PropertiesSupplier<ConsumerIdentifier, ConsumerProperties>> suppliers,
119123
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
120124
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials,
121125
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder>> customizers,
122126
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder.ServiceBusSessionReceiverClientBuilder>> sessionReceiverCustomizers) {
123127
DefaultServiceBusNamespaceConsumerFactory factory = new DefaultServiceBusNamespaceConsumerFactory(properties, suppliers.getIfAvailable());
128+
factory.setApplicationContext(applicationContext);
124129
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
125130
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
126131
customizers.orderedStream().forEach(factory::addServiceBusClientBuilderCustomizer);
@@ -136,12 +141,14 @@ static class ServiceBusTemplateConfiguration {
136141
@ConditionalOnMissingBean
137142
ServiceBusProducerFactory defaultServiceBusNamespaceProducerFactory(
138143
NamespaceProperties properties,
144+
ApplicationContext applicationContext,
139145
ObjectProvider<PropertiesSupplier<String, ProducerProperties>> suppliers,
140146
ObjectProvider<AzureTokenCredentialResolver> tokenCredentialResolvers,
141147
@Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) ObjectProvider<TokenCredential> defaultTokenCredentials,
142148
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder>> clientBuilderCustomizers,
143149
ObjectProvider<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder.ServiceBusSenderClientBuilder>> senderClientBuilderCustomizers) {
144150
DefaultServiceBusNamespaceProducerFactory factory = new DefaultServiceBusNamespaceProducerFactory(properties, suppliers.getIfAvailable());
151+
factory.setApplicationContext(applicationContext);
145152
factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable());
146153
factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable());
147154
clientBuilderCustomizers.orderedStream().forEach(factory::addServiceBusClientBuilderCustomizer);

sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsMessagingAutoConfigurationTests.java

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import com.azure.core.credential.TokenCredential;
77
import com.azure.messaging.eventhubs.CheckpointStore;
8+
import com.azure.messaging.eventhubs.EventHubProducerAsyncClient;
89
import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration;
910
import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration;
1011
import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.configuration.TestCheckpointStore;
@@ -148,48 +149,43 @@ void testCustomTokenCredentialConfiguration() {
148149
AzureTokenCredentialAutoConfiguration.class,
149150
AzureGlobalPropertiesAutoConfiguration.class))
150151
.withBean(EventHubsMessageConverter.class, EventHubsMessageConverter::new)
152+
.withBean(CheckpointStore.class, TestCheckpointStore::new)
151153
.withPropertyValues(
152154
"spring.cloud.azure.eventhubs.connection-string=" + String.format(CONNECTION_STRING_FORMAT, "test-namespace"),
153155
"spring.cloud.azure.eventhubs.credential.token-credential-bean-name=customTokenCredential"
154156
)
155157
.withUserConfiguration(AzureEventHubsPropertiesTestConfiguration.class)
156158
.run(context -> {
157-
158-
// Verify that the properties contain the correct credential bean name
159+
TokenCredential customCredential = context.getBean("customTokenCredential", TokenCredential.class);
159160
AzureEventHubsProperties eventHubsProperties = context.getBean(AzureEventHubsProperties.class);
160-
assertThat(eventHubsProperties).isNotNull();
161-
assertThat(eventHubsProperties.getCredential()).isNotNull();
161+
162162
assertThat(eventHubsProperties.getCredential().getTokenCredentialBeanName())
163-
.as("The token-credential-bean-name property should be set to customTokenCredential")
164163
.isEqualTo("customTokenCredential");
165164

166-
// Verify that the custom token credential bean exists
167-
assertThat(context).hasBean("customTokenCredential");
168-
TokenCredential customCredential = context.getBean("customTokenCredential", TokenCredential.class);
169-
assertThat(customCredential).isNotNull();
170-
171-
// Verify the EventHubsProducerFactory has the tokenCredentialResolver configured
172-
assertThat(context).hasSingleBean(EventHubsProducerFactory.class);
173165
EventHubsProducerFactory producerFactory = context.getBean(EventHubsProducerFactory.class);
174-
assertThat(producerFactory).isNotNull();
175-
176-
// Verify tokenCredentialResolver resolves to the custom credential
177-
Field tokenCredentialResolverField = producerFactory.getClass().getDeclaredField("tokenCredentialResolver");
178-
tokenCredentialResolverField.setAccessible(true);
179-
Object tokenCredentialResolver = tokenCredentialResolverField.get(producerFactory);
180-
assertThat(tokenCredentialResolver).as("TokenCredentialResolver should be configured").isNotNull();
181-
182-
// Cast to AzureCredentialResolver and invoke resolve() to verify it returns customTokenCredential
183-
@SuppressWarnings("unchecked")
184-
AzureCredentialResolver<TokenCredential> resolver =
185-
(AzureCredentialResolver<TokenCredential>) tokenCredentialResolver;
186-
TokenCredential resolvedCredential = resolver.resolve(eventHubsProperties);
187-
assertThat(resolvedCredential)
188-
.as("The resolved credential should be the customTokenCredential bean")
166+
EventHubsProcessorFactory processorFactory = context.getBean(EventHubsProcessorFactory.class);
167+
168+
// Validate credential resolution - without ApplicationContext propagation fix,
169+
// tokenCredentialBeanName would be silently ignored and connection string would be used
170+
assertThat(resolveCredential(producerFactory, eventHubsProperties))
171+
.isSameAs(customCredential);
172+
assertThat(resolveCredential(processorFactory, eventHubsProperties))
189173
.isSameAs(customCredential);
174+
175+
// Validate runtime producer creation
176+
EventHubProducerAsyncClient producer = producerFactory.createProducer("test-eventhub");
177+
producer.close();
190178
});
191179
}
192180

181+
@SuppressWarnings("unchecked")
182+
private TokenCredential resolveCredential(Object factory, AzureEventHubsProperties properties) throws Exception {
183+
Field field = factory.getClass().getDeclaredField("tokenCredentialResolver");
184+
field.setAccessible(true);
185+
AzureCredentialResolver<TokenCredential> resolver = (AzureCredentialResolver<TokenCredential>) field.get(factory);
186+
return resolver.resolve(properties);
187+
}
188+
193189
@Configuration
194190
public static class CustomTokenCredentialConfiguration {
195191
@Bean

sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfigurationTests.java

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration;
99
import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties;
1010
import com.azure.spring.cloud.core.credential.AzureCredentialResolver;
11+
import com.azure.spring.cloud.service.servicebus.properties.ServiceBusEntityType;
12+
import com.azure.spring.messaging.servicebus.core.ServiceBusConsumerFactory;
1113
import com.azure.spring.messaging.servicebus.core.ServiceBusProcessorFactory;
1214
import com.azure.spring.messaging.servicebus.core.ServiceBusProducerFactory;
1315
import com.azure.spring.messaging.servicebus.core.ServiceBusTemplate;
@@ -150,44 +152,36 @@ void testCustomTokenCredentialConfiguration() {
150152
)
151153
.withUserConfiguration(AzureServiceBusPropertiesTestConfiguration.class)
152154
.run(context -> {
153-
154-
// Verify that the properties contain the correct credential bean name
155+
TokenCredential customCredential = context.getBean("customTokenCredential", TokenCredential.class);
155156
AzureServiceBusProperties serviceBusProperties = context.getBean(AzureServiceBusProperties.class);
156-
assertThat(serviceBusProperties).isNotNull();
157-
assertThat(serviceBusProperties.getCredential()).isNotNull();
157+
158158
assertThat(serviceBusProperties.getCredential().getTokenCredentialBeanName())
159-
.as("The token-credential-bean-name property should be set to customTokenCredential")
160159
.isEqualTo("customTokenCredential");
161160

162-
// Verify that the custom token credential bean exists
163-
assertThat(context).hasBean("customTokenCredential");
164-
TokenCredential customCredential = context.getBean("customTokenCredential", TokenCredential.class);
165-
assertThat(customCredential).isNotNull();
166-
167-
// Verify the ServiceBusProducerFactory has the tokenCredentialResolver configured
168-
assertThat(context).hasSingleBean(ServiceBusProducerFactory.class);
169161
ServiceBusProducerFactory producerFactory = context.getBean(ServiceBusProducerFactory.class);
170-
assertThat(producerFactory).isNotNull();
171-
172-
// Verify tokenCredentialResolver resolves to the custom credential
173-
Field tokenCredentialResolverField =
174-
producerFactory.getClass().getDeclaredField("tokenCredentialResolver");
175-
tokenCredentialResolverField.setAccessible(true);
176-
Object tokenCredentialResolver = tokenCredentialResolverField.get(producerFactory);
177-
assertThat(tokenCredentialResolver)
178-
.as("TokenCredentialResolver should be configured").isNotNull();
179-
180-
// Cast to AzureCredentialResolver and invoke resolve() to verify it returns customTokenCredential
181-
@SuppressWarnings("unchecked")
182-
AzureCredentialResolver<TokenCredential> resolver =
183-
(AzureCredentialResolver<TokenCredential>) tokenCredentialResolver;
184-
TokenCredential resolvedCredential = resolver.resolve(serviceBusProperties);
185-
assertThat(resolvedCredential)
186-
.as("The resolved credential should be the customTokenCredential bean")
162+
ServiceBusProcessorFactory processorFactory = context.getBean(ServiceBusProcessorFactory.class);
163+
ServiceBusConsumerFactory consumerFactory = context.getBean(ServiceBusConsumerFactory.class);
164+
165+
assertThat(resolveCredential(producerFactory, serviceBusProperties))
187166
.isSameAs(customCredential);
167+
assertThat(resolveCredential(processorFactory, serviceBusProperties))
168+
.isSameAs(customCredential);
169+
assertThat(resolveCredential(consumerFactory, serviceBusProperties))
170+
.isSameAs(customCredential);
171+
172+
// Validate runtime producer creation
173+
producerFactory.createProducer("test-queue", ServiceBusEntityType.QUEUE).close();
188174
});
189175
}
190176

177+
@SuppressWarnings("unchecked")
178+
private TokenCredential resolveCredential(Object factory, AzureServiceBusProperties properties) throws Exception {
179+
Field field = factory.getClass().getDeclaredField("tokenCredentialResolver");
180+
field.setAccessible(true);
181+
AzureCredentialResolver<TokenCredential> resolver = (AzureCredentialResolver<TokenCredential>) field.get(factory);
182+
return resolver.resolve(properties);
183+
}
184+
191185
@Configuration
192186
public static class CustomTokenCredentialConfiguration {
193187
@Bean

0 commit comments

Comments
 (0)