Skip to content

Support to inherit ServiceBusClientBuilder configurations#49777

Open
moarychan wants to merge 13 commits into
mainfrom
v-shilichen/fix-srcbus-sub-client-override-parent-client-options
Open

Support to inherit ServiceBusClientBuilder configurations#49777
moarychan wants to merge 13 commits into
mainfrom
v-shilichen/fix-srcbus-sub-client-override-parent-client-options

Conversation

@moarychan

@moarychan moarychan commented Jul 10, 2026

Copy link
Copy Markdown
Member

Description

Fixes #49742

All SDK Contribution checklist:

  • The pull request does not introduce [breaking changes]
  • CHANGELOG is updated for new features, bug fixes or other significant changes.
  • I have read the contribution guidelines.

General Guidelines and Best Practices

  • Title of the pull request is clear and informative.
  • There are a small number of commits, each of which have an informative message. This means that previously merged commits do not appear in the history of the PR. For more information on cleaning up the commits in your PR, see this page.

Testing Guidelines

  • Pull request includes test coverage for the included changes.

@github-actions github-actions Bot added the azure-spring All azure-spring related issues label Jul 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request addresses #49742 by ensuring Service Bus processor (and other sub-client) creation can preserve ServiceBusClientBuilder configuration applied via AzureServiceClientBuilderCustomizer (notably ClientOptions/TracingOptions) instead of having it overwritten during sub-client builder configuration.

Changes:

  • Introduces a new inherit-configuration property (nullable, treated as true by default) and propagates it through Service Bus namespace/producer/consumer/processor property models and mergers.
  • Updates Service Bus sub-client builder factory behavior to optionally skip re-applying property-derived configuration to the underlying ServiceBusClientBuilder, preventing customizer configuration from being overwritten.
  • Aligns listener container factory bean name constants/usages and adds unit tests for the new inheritance behavior.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
sdk/spring/spring-messaging-azure/src/main/java/com/azure/spring/messaging/implementation/config/AzureListenerEndpoint.java Fixes minor grammar in interface JavaDoc.
sdk/spring/spring-messaging-azure-servicebus/src/test/java/com/azure/spring/messaging/servicebus/implementation/properties/merger/ProcessorPropertiesParentMergerTests.java Adds coverage for inheritConfiguration precedence (parent vs child).
sdk/spring/spring-messaging-azure-servicebus/src/test/java/com/azure/spring/messaging/servicebus/implementation/properties/merger/ConsumerPropertiesParentMergerTests.java Adds coverage for inheritConfiguration precedence (parent vs child).
sdk/spring/spring-messaging-azure-servicebus/src/main/java/com/azure/spring/messaging/servicebus/implementation/properties/merger/SenderPropertiesParentMerger.java Propagates inheritConfiguration from parent/child into merged producer properties.
sdk/spring/spring-messaging-azure-servicebus/src/main/java/com/azure/spring/messaging/servicebus/implementation/properties/merger/ProcessorPropertiesParentMerger.java Propagates inheritConfiguration from namespace parent into processor properties.
sdk/spring/spring-messaging-azure-servicebus/src/main/java/com/azure/spring/messaging/servicebus/implementation/properties/merger/ProcessorPropertiesMerger.java Includes inheritConfiguration in processor property copying logic.
sdk/spring/spring-messaging-azure-servicebus/src/main/java/com/azure/spring/messaging/servicebus/implementation/properties/merger/ConsumerPropertiesParentMerger.java Propagates inheritConfiguration into merged consumer properties and copy helper.
sdk/spring/spring-messaging-azure-servicebus/src/main/java/com/azure/spring/messaging/servicebus/implementation/core/annotation/ServiceBusListenerAnnotationBeanPostProcessor.java Exposes default container factory bean name constant publicly for reuse.
sdk/spring/spring-messaging-azure-servicebus/src/main/java/com/azure/spring/messaging/servicebus/implementation/core/annotation/ServiceBusListener.java Fixes documented default container factory bean name to match actual default.
sdk/spring/spring-messaging-azure-servicebus/src/main/java/com/azure/spring/messaging/servicebus/core/properties/CommonProperties.java Adds inheritConfiguration property to common Service Bus properties.
sdk/spring/spring-messaging-azure-eventhubs/src/main/java/com/azure/spring/messaging/eventhubs/implementation/core/annotation/EventHubsListenerAnnotationBeanPostProcessor.java Exposes default container factory bean name constant publicly for reuse.
sdk/spring/spring-messaging-azure-eventhubs/src/main/java/com/azure/spring/messaging/eventhubs/implementation/core/annotation/EventHubsListener.java Fixes documented default container factory bean name to match actual default.
sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/servicebus/properties/ServiceBusClientCommonTestProperties.java Adds inheritConfiguration to test properties used by factory tests.
sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/servicebus/factory/AbstractServiceBusSubClientBuilderFactoryTests.java Adds tests ensuring inheritConfiguration=false prevents overwriting shared builder config.
sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/servicebus/properties/ServiceBusClientCommonProperties.java Adds new getInheritConfiguration() contract and documents null-as-true behavior.
sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/servicebus/lifecycle/ServiceBusProcessorClientLifecycleManager.java Fixes a minor grammar issue in class JavaDoc.
sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/servicebus/factory/AbstractServiceBusSubClientBuilderFactory.java Implements inheritance gating to prevent property-derived overwrites of ServiceBusClientBuilder config.
sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusPropertiesTest.java Adds tests for default null and inheritance/override behavior in autoconfig properties.
sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusProperties.java Propagates inheritConfiguration into built producer/consumer/processor property objects.
sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/properties/AzureServiceBusCommonProperties.java Adds namespace-level inheritConfiguration property to autoconfig model.
sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/messaging/AzureMessagingListenerAutoConfiguration.java Reuses exposed constants for container factory bean naming to avoid string duplication.
sdk/spring/CHANGELOG.md Documents the new inherit-configuration property and its intended effect.

moarychan and others added 2 commits July 10, 2026 14:24
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@moarychan moarychan marked this pull request as ready for review July 10, 2026 06:25
@moarychan moarychan requested review from a team, Netyyyy, rujche and saragluna as code owners July 10, 2026 06:25
@rujche rujche requested a review from Copilot July 10, 2026 06:27
@rujche rujche moved this to In Progress in Spring Cloud Azure Jul 10, 2026
@rujche rujche added this to the 2026-08 milestone Jul 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 2 comments.

Comment thread sdk/spring/CHANGELOG.md Outdated
Comment on lines 39 to +50
propertyMapper.from(parent.getEntityName()).to(properties::setEntityName);
propertyMapper.from(parent.getEntityType()).to(properties::setEntityType);
propertyMapper.from(parent.getCustomEndpointAddress()).to(properties::setCustomEndpointAddress);
propertyMapper.from(parent.getInheritConfiguration()).to(properties::setInheritConfiguration);

propertyMapper.from(child.getDomainName()).to(properties::setDomainName);
propertyMapper.from(child.getNamespace()).to(properties::setNamespace);
propertyMapper.from(child.getConnectionString()).to(properties::setConnectionString);
propertyMapper.from(child.getEntityName()).to(properties::setEntityName);
propertyMapper.from(child.getEntityType()).to(properties::setEntityType);
propertyMapper.from(child.getCustomEndpointAddress()).to(properties::setCustomEndpointAddress);
propertyMapper.from(child.getInheritConfiguration()).to(properties::setInheritConfiguration);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added inheritConfigurationShouldFollowParentChildPrecedence to ProducerPropertiesParentMergerTests in 0c0c04c, mirroring the consumer/processor merger tests.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: moarychan <19926623+moarychan@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 5 comments.

Comment on lines 15 to 18
* This class implements {@code SmartLifecycle} to auto start {@link ServiceBusProcessorClient} when the Spring Application Context starts,
* And stop the {@link ServiceBusProcessorClient} when the Spring Application Context stops.
* NOTE, there is not need to call {@link #start()} or {@link #stop()} explicitly, as the {@link ServiceBusProcessorClient} will be started and stopped automatically.
* NOTE, there is no need to call {@link #start()} or {@link #stop()} explicitly, as the {@link ServiceBusProcessorClient} will be started and stopped automatically.
* And since the {@link ServiceBusProcessorClient} is a {@link AutoCloseable}, there is no need to call {@link ServiceBusProcessorClient#close()} explicitly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reflowed the JavaDoc block to keep each line within the 120-character Checkstyle limit in 9349e3c.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
moarychan and others added 3 commits July 10, 2026 16:42
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…hars

Co-authored-by: moarychan <19926623+moarychan@users.noreply.github.com>
@rujche

rujche commented Jul 10, 2026

Copy link
Copy Markdown
Member

Alternative Solution: Restore Customizers in build() Method

I'd like to suggest an alternative approach that might be simpler and more elegant than introducing the inherit-configuration property.

Problem Analysis

The root cause is that in the non-shared path (e.g., @ServiceBusListener processors created by DefaultServiceBusNamespaceProcessorFactory):

  1. Customizers are applied to the root ServiceBusClientBuilder during serviceBusClientBuilderFactory.build()
  2. Then configureCore() is called on the processor sub-builder, which overwrites the root builder's clientOptions (and other configurations) via methods like consumeClientOptions()
  3. This causes the customizer's configuration (e.g., TracingOptions) to be silently discarded

Proposed Solution

Instead of introducing a new inherit-configuration property, we can:

  1. Save the customizers in AbstractServiceBusSubClientBuilderFactory constructor
  2. Restore them once in the overridden build() method after all configurations are complete

Implementation:

abstract class AbstractServiceBusSubClientBuilderFactory<T, P extends ServiceBusClientCommonProperties> extends AbstractAzureAmqpClientBuilderFactory<T> {
    
    private ServiceBusClientBuilder serviceBusClientBuilder;
    private final boolean shareServiceBusClientBuilder;
    private ServiceBusClientBuilderFactory serviceBusClientBuilderFactory;
    private List<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder>> serviceBusClientBuilderCustomizers;  // ← Save reference

    protected AbstractServiceBusSubClientBuilderFactory(ServiceBusClientBuilder serviceBusClientBuilder,
                                              P properties,
                                              List<AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder>> serviceBusClientBuilderCustomizers) {
        this.properties = properties;
        this.serviceBusClientBuilderCustomizers = serviceBusClientBuilderCustomizers;  // ← Store reference
        
        if (serviceBusClientBuilder != null) {
            this.serviceBusClientBuilder = serviceBusClientBuilder;
            this.shareServiceBusClientBuilder = true;
            this.serviceBusClientBuilderFactory = null;
        } else {
            this.serviceBusClientBuilderFactory = new ServiceBusClientBuilderFactory(properties);
            if (serviceBusClientBuilderCustomizers != null) {
                serviceBusClientBuilderCustomizers.forEach(this.serviceBusClientBuilderFactory::addBuilderCustomizer);
            }
            this.serviceBusClientBuilder = null;
            this.shareServiceBusClientBuilder = false;
        }
    }
    
    @Override
    public T build() {
        T builder = super.build();  // Execute all parent configurations
        
        // Restore customizers in non-shared path to prevent their configs from being overwritten
        if (!isShareServiceBusClientBuilder() && serviceBusClientBuilderCustomizers != null) {
            serviceBusClientBuilderCustomizers.forEach(c -> c.customize(getServiceBusClientBuilder()));
        }
        
        return builder;
    }
}

Advantages Over Current Approach

1. No New Configuration Property

  • Current: Requires users to understand and configure spring.cloud.azure.servicebus.processor.inherit-configuration=false
  • Proposed: Customizers always work as intended, zero configuration needed

2. Customizers Execute Only Once

  • Current: Customizers are already added to serviceBusClientBuilderFactory, so they execute during root builder creation
  • Proposed: Only add one more execution at the end to restore, minimal overhead
  • No changes needed to multiple consumeXxx() methods

3. Clearer Intent

  • Current: The inherit-configuration flag is somewhat opaque - users need to understand what "not inheriting" means
  • Proposed: The logic is explicit - "after all configurations, restore customizers" - anyone reading the code can understand it immediately

4. Better Compatibility

  • Current: Default behavior changes (must set inherit-configuration=true to restore old behavior), existing code might break
  • Proposed: Works transparently without requiring user changes or understanding the internal mechanism

5. Follows Principle of Least Surprise

  • Customizers are documented as "the extension point for customizing the underlying Service Bus client builder"
  • Current approach: Customizer config can still be overwritten silently - violates the principle
  • Proposed: Customizer config is always applied and preserved - matches user expectations

Example Use Case

@Configuration
public class MyConfig {
    @Bean
    public AzureServiceClientBuilderCustomizer<ServiceBusClientBuilder> customizer() {
        return builder -> builder.clientOptions(new ClientOptions().setApplicationId("myapp-1.0")
            .setTracingOptions(new TracingOptions()));  // User expects this to be applied
    }
}

@Component
public class MyListener {
    @ServiceBusListener(destination = "myqueue")
    public void handleMessage(String message) {
        // In the proposed solution, the customizer will be applied
        // In the current solution without inherit-configuration=false, it might be overwritten
    }
}

Trade-offs

  • Customizers on the root builder will execute twice (once during root build, once during restoration)
    • However, this is explicitly for overwriting configs set by configureCore(), so it's semantically correct
    • Most customizers are idempotent anyway

I believe this approach is cleaner and more intuitive. Would you consider this as an alternative to the inherit-configuration property?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

azure-spring All azure-spring related issues

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

[BUG] spring-cloud-azure: customizer-set ClientOptions (incl. TracingOptions) silently overwritten for Service Bus processors

4 participants