Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Feb 10, 2026

Plan: Change sender's default JmsConnectionFactory to CachingConnectionFactory

Understanding:

  • Currently, when spring.jms.servicebus.pool.enabled and spring.jms.cache.enabled are not set, both sender and receiver use ServiceBusJmsConnectionFactory
  • This is inefficient for senders using JmsTemplate as each send creates and closes a connection
  • The fix: Use CachingConnectionFactory by default for the sender side

Implementation Strategy:

  • Modify ServiceBusJmsConnectionFactoryConfiguration.java to implement complete configuration matrix
  • Modify ServiceBusJmsContainerConfiguration.java to match all scenarios
  • Add comprehensive test coverage for all 9 scenarios
  • Add JavaDoc tables to sender and receiver configurations
  • Update CHANGELOG.md with detailed configuration table and rules
  • Implement proper fallback logic when required classes not in classpath
  • All tests passing (77 JMS tests)
  • Fix checkstyle linting errors (operator wrapping)
  • Address all code review feedback:
    • Improved fallback logic to immediately return when requested classes missing
    • Memoized ALL dedicated receiver ConnectionFactory types to avoid duplicate instances
    • Enhanced fallback tests to validate real scenarios
    • Implemented lifecycle management with DisposableBean
    • Fixed CHANGELOG typo
    • Added comprehensive receiver ConnectionFactory tests verifying actual types
    • Extracted shared helper method to avoid code duplication
    • Improved integration test type assertion robustness
Original prompt

This section details on the original issue you should resolve

<issue_title>[FEATURE REQ] Change sender's default JmsConnectionFactory from ServiceBusJmsConnectionFactory to CachingConnectionFactory</issue_title>
<issue_description>## Current behavior

Property (spring.jms.servicebus.pool.enabled and spring.jms.cache.enabled) not set or set to false Property (spring.jms.servicebus.pool.enabled or spring.jms.cache.enabled) set to true
sender side ServiceBusJmsConnectionFactory CachingConnectionFactory or JmsPoolConnectionFactory
receiver side ServiceBusJmsConnectionFactory CachingConnectionFactory or JmsPoolConnectionFactory

Expected behavior

Comment from @vinaysurya

If we don't use a CachingConnectionFactory on Sender side, I'm afraid when using JMSTemplate every send from Spring SDK will create and close a connection which will be very inefficient.

Property (spring.jms.servicebus.pool.enabled and spring.jms.cache.enabled) not set or set to false Property (spring.jms.servicebus.pool.enabled or spring.jms.cache.enabled) set to true
sender side CachingConnectionFactory CachingConnectionFactory or JmsPoolConnectionFactory
receiver side ServiceBusJmsConnectionFactory CachingConnectionFactory or JmsPoolConnectionFactory

Relate codes

  1. JmsListenerContainerFactory<?> jmsListenerContainerFactory(
    DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
    DefaultJmsListenerContainerFactory jmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
    configurer.configure(jmsListenerContainerFactory, connectionFactory);
    jmsListenerContainerFactory.setPubSubDomain(Boolean.FALSE);
    configureCommonListenerContainerFactory(jmsListenerContainerFactory);
    return jmsListenerContainerFactory;
    }
    @Bean
    @ConditionalOnMissingBean(name = "topicJmsListenerContainerFactory")
    JmsListenerContainerFactory<?> topicJmsListenerContainerFactory(
    DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
    DefaultJmsListenerContainerFactory jmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
    configurer.configure(jmsListenerContainerFactory, connectionFactory);
    jmsListenerContainerFactory.setPubSubDomain(Boolean.TRUE);
    configureCommonListenerContainerFactory(jmsListenerContainerFactory);
    configureTopicListenerContainerFactory(jmsListenerContainerFactory);
    return jmsListenerContainerFactory;
    }
  2. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
    BeanDefinitionRegistry registry) {
    BindResult<Boolean> poolEnabledResult = Binder.get(environment).bind("spring.jms.servicebus.pool.enabled", Boolean.class);
    BindResult<Boolean> cacheEnabledResult = Binder.get(environment).bind("spring.jms.cache.enabled", Boolean.class);
    if (isPoolConnectionFactoryClassPresent() && poolEnabledResult.orElseGet(() -> false)) {
    registerJmsPoolConnectionFactory(registry);
    return;
    }
    if (isCacheConnectionFactoryClassPresent() && cacheEnabledResult.orElseGet(() -> false)) {
    registerJmsCachingConnectionFactory(registry);
    return;
    }
    </issue_description>

Comments on the Issue (you are @copilot in this section)

@rujche ## Current status
  1. From azure-spring-boot-samples, the sender is JmsTemplate bean.
  2. The sender side, related code is in spring-boot/../JmsClientConfigurations.java, the JmsTemplate bean will only be configured when there is only one bean with type ConnectionFactory. @ConditionalOnSingleCandidate(ConnectionFactory.class).
  3. The receiver side, related code is in azure-sdk-for-java/.../ServiceBusJmsContainerConfiguration, it also uses the bean ConnectionFactory, it's the same one used in spring-boot/../JmsClientConfigurations.java

Implementation suggestion

  1. Keep the configuration with only one bean with type ConnectionFactory, to make the spring-boot/../JmsClientConfigurations.java can still work.
  2. On the sender side, in [azure-sdk-for-java/.../ServiceBusJmsConnectionFactoryConfiguration](https://github.com/Azure/azure-sdk-for-java/blob/be5923f94284df7f971c59785322a596fde1b37e/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/...

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 3 commits February 10, 2026 02:14
…iver ConnectionFactory

Co-authored-by: rujche <171773178+rujche@users.noreply.github.com>
Co-authored-by: rujche <171773178+rujche@users.noreply.github.com>
Co-authored-by: rujche <171773178+rujche@users.noreply.github.com>
Copilot AI changed the title [WIP] Change sender's default JmsConnectionFactory to CachingConnectionFactory Use CachingConnectionFactory by default for Service Bus JMS sender operations Feb 10, 2026
Copilot AI requested a review from rujche February 10, 2026 02:22
The receiver now conditionally uses the ConnectionFactory bean when it's
JmsPoolConnectionFactory or CachingConnectionFactory, otherwise creates a
dedicated ServiceBusJmsConnectionFactory. This matches the expected behavior
where the receiver should use the same pooled/cached connection factory as
the sender when those features are enabled.

Co-authored-by: rujche <171773178+rujche@users.noreply.github.com>
Copilot AI requested a review from rujche February 10, 2026 02:39
The receiver now only uses the shared ConnectionFactory bean when pooling
or caching is EXPLICITLY enabled. When properties are not set, the receiver
creates a dedicated ServiceBusJmsConnectionFactory instance, even though
the sender defaults to CachingConnectionFactory. This matches the expected
behavior table from the issue.

Co-authored-by: rujche <171773178+rujche@users.noreply.github.com>
Copilot AI requested a review from rujche February 10, 2026 02:51
@rujche
Copy link
Member

rujche commented Feb 10, 2026

Table

spring.jms.servicebus.pool.enabled value spring.jms.cache.enabled value sender receiver case number
not set not set CachingConnectionFactory ServiceBusJmsConnectionFactory 4
not set true CachingConnectionFactory CachingConnectionFactory 1
not set false ServiceBusJmsConnectionFactory ServiceBusJmsConnectionFactory 3
true not set JmsPoolConnectionFactory JmsPoolConnectionFactory 1
true true CachingConnectionFactory CachingConnectionFactory 2
true false JmsPoolConnectionFactory JmsPoolConnectionFactory 1
false not set CachingConnectionFactory ServiceBusJmsConnectionFactory 4
false true CachingConnectionFactory CachingConnectionFactory 1
false false ServiceBusJmsConnectionFactory ServiceBusJmsConnectionFactory 3

Explanation

  1. If only one of the properties (spring.jms.servicebus.pool.enabled and spring.jms.cache.enabled) set to true, set both sender and receiver to that factory. (4 cases)
  2. If both properties set to true, set both sender and receiver to CachingConnectionFactory. (1 case)
  3. Otherwise, if spring.jms.cache.enabled set to false, set both sender and receiver to ServiceBusJmsConnectionFactory . (2 case)
  4. Otherwise, use default value: set sender to CachingConnectionFactory, set receiver to ServiceBusJmsConnectionFactory. (2 cases)
  5. When use CachingConnectionFactory or JmsPoolConnectionFactory, confirm related class in the classpath. If it's not in the classpath, fallback to ServiceBusJmsConnectionFactory.

@copilot , According to this table and explanation:

  1. Update related code.
  2. Update test code, enrich test case.
  3. Update change log, add the table to change log to make it more clear.
  4. Add the table into related java doc to make it clear. In sender's java doc, only add sender in the table, in the receiver's java doc, only add receiver in the doc.

- Updated logic to follow the complete configuration table with all 9 scenarios
- Added JavaDoc tables to both sender and receiver configurations
- Added comprehensive test coverage for all scenarios
- Updated CHANGELOG with detailed configuration table and rules
- Both properties set to true now uses CachingConnectionFactory (cache takes precedence)
- Added proper fallback logic when required classes are not in classpath

Co-authored-by: rujche <171773178+rujche@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 10, 2026

Table

| spring.jms.servicebus.pool.enabled value | spring.jms.cache.enabled value | sender | receiver | case number |
...

Done in commit 29cb8b3:

  1. Updated related code - Implemented the complete logic table with all 9 scenarios for both sender and receiver
  2. Enriched test cases - Added 7 new test cases covering all property combinations
  3. Updated CHANGELOG - Added the complete table with all scenarios and 5 rules explaining the behavior
  4. Added JavaDoc tables - Both ServiceBusJmsConnectionFactoryConfiguration (sender) and ServiceBusJmsContainerConfiguration (receiver) now have JavaDoc tables showing only their respective ConnectionFactory types

Key changes:

  • When both properties are true → CachingConnectionFactory (cache takes precedence)
  • When cache.enabled=false → checks pool.enabled for JmsPoolConnectionFactory vs ServiceBusJmsConnectionFactory
  • Proper fallback when required classes not in classpath
  • All 68 JMS tests passing

Copilot AI changed the title Use CachingConnectionFactory by default for Service Bus JMS sender operations Implement comprehensive JMS ConnectionFactory configuration for Service Bus Feb 10, 2026
@rujche
Copy link
Member

rujche commented Feb 10, 2026

@copilot Fix the lint error:

2026-02-10T08:59:38.1667963Z ##[section]Starting: Generate Linting Reports
2026-02-10T08:59:38.1673984Z ==============================================================================
2026-02-10T08:59:38.1674245Z Task         : PowerShell
2026-02-10T08:59:38.1674382Z Description  : Run a PowerShell script on Linux, macOS, or Windows
2026-02-10T08:59:38.1674517Z Version      : 2.268.1
2026-02-10T08:59:38.1674645Z Author       : Microsoft Corporation
2026-02-10T08:59:38.1674758Z Help         : https://docs.microsoft.com/azure/devops/pipelines/tasks/utility/powershell
2026-02-10T08:59:38.1674900Z ==============================================================================
2026-02-10T08:59:38.2973795Z Generating script.
2026-02-10T08:59:38.2987577Z ========================== Starting Command Output ===========================
2026-02-10T08:59:38.3000052Z [command]/usr/bin/pwsh -NoLogo -NoProfile -NonInteractive -Command . '/mnt/vss/_work/_temp/83345fb9-d951-4514-b781-8c89e93550e7.ps1'
2026-02-10T08:59:43.5087035Z 
2026-02-10T08:59:43.5148381Z File Name,Line Number,Column Number,Message,Type
2026-02-10T08:59:43.5149175Z "/mnt/vss/_work/1/s/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsContainerConfiguration.java",101,72,"'&&' should be on a new line.","com.puppycrawl.tools.checkstyle.checks.whitespace.OperatorWrapCheck"
2026-02-10T08:59:43.5150228Z "/mnt/vss/_work/1/s/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsContainerConfiguration.java",110,70,"'&&' should be on a new line.","com.puppycrawl.tools.checkstyle.checks.whitespace.OperatorWrapCheck"
2026-02-10T08:59:43.5151038Z "/mnt/vss/_work/1/s/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsContainerConfiguration.java",116,68,"'&&' should be on a new line.","com.puppycrawl.tools.checkstyle.checks.whitespace.OperatorWrapCheck"
2026-02-10T08:59:43.5151816Z "/mnt/vss/_work/1/s/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsContainerConfiguration.java",117,43,"'&&' should be on a new line.","com.puppycrawl.tools.checkstyle.checks.whitespace.OperatorWrapCheck"
2026-02-10T08:59:43.5152171Z 
2026-02-10T08:59:43.8298576Z Linting errors have been found. Navigate to https://dev.azure.com/azure-sdk/public/_build/results?buildId=5859617&view=artifacts&pathAsName=false&type=publishedArtifacts and download the 'linting-report.zip' artifact to see what was found.
2026-02-10T08:59:43.8316634Z �[32;1mCapacity�[0m�[32;1m MaxCapacity�[0m�[32;1m Length�[0m
2026-02-10T08:59:43.8317460Z �[32;1m--------�[0m �[32;1m-----------�[0m �[32;1m------�[0m
2026-02-10T08:59:43.8317947Z      568  2147483647    285
2026-02-10T08:59:43.8318350Z     1136  2147483647    570
2026-02-10T08:59:43.8318688Z     1136  2147483647    855
2026-02-10T08:59:43.8319051Z     2272  2147483647   1140
2026-02-10T08:59:43.8319377Z     2321  2147483647   1189
2026-02-10T08:59:43.8320027Z 
2026-02-10T08:59:43.8837982Z 
2026-02-10T08:59:43.8871651Z ##[error]PowerShell exited with code '1'.
2026-02-10T08:59:43.8920855Z ##[section]Finishing: Generate Linting Reports

Fixed OperatorWrapCheck violations by moving && operators from end of line
to the beginning of the next line, following the project's code style rules.

Co-authored-by: rujche <171773178+rujche@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 10, 2026

@copilot Fix the lint error:

...

Fixed in commit 42d8270. Moved all && operators from the end of lines to the beginning of the next line to comply with OperatorWrapCheck. Checkstyle now passes with 0 violations.

@rujche
Copy link
Member

rujche commented Feb 11, 2026

@copilot apply changes based on the comments in this thread

1. Fixed CHANGELOG typo: Removed extra comma after "Note:"
2. Enhanced receiver tests to verify actual ConnectionFactory instances:
   - Create DefaultMessageListenerContainer from factories
   - Assert ConnectionFactory type using getConnectionFactory()
   - Verify dedicated instances are NOT same as sender bean
   - Verify shared beans ARE same as sender bean
   - Verify both listener containers share same dedicated instance (memoization)

Tests now properly validate that receiver uses correct ConnectionFactory type
and that memoization prevents duplicate instances.

Co-authored-by: rujche <171773178+rujche@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 11, 2026

@copilot apply changes based on the comments in this thread

Applied in commit 0b12dc2. Enhanced all 4 receiver tests to:

  1. Create DefaultMessageListenerContainer from each factory
  2. Assert actual ConnectionFactory type using getConnectionFactory()
  3. Verify dedicated instances are NOT same as sender bean (using isNotSameAs())
  4. Verify shared beans ARE same as sender bean (using isSameAs())
  5. Verify both queue and topic containers share same dedicated instance (validates memoization)

Tests now properly validate receiver ConnectionFactory behavior and instance sharing.

Copy link
Contributor

Copilot AI left a comment

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 5 out of 5 changed files in this pull request and generated no new comments.

@rujche rujche marked this pull request as ready for review February 11, 2026 08:36
@rujche rujche requested review from a team, Netyyyy, moarychan and saragluna as code owners February 11, 2026 08:36
@rujche
Copy link
Member

rujche commented Feb 11, 2026

/azp run java - spring - tests

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

…b.com:Azure/azure-sdk-for-java into copilot/change-default-jms-connection-factory
@rujche
Copy link
Member

rujche commented Feb 12, 2026

/azp run java - spring - tests

@rujche rujche requested a review from Copilot February 12, 2026 02:43
@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Contributor

Copilot AI left a comment

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 6 out of 6 changed files in this pull request and generated 2 comments.

@rujche
Copy link
Member

rujche commented Feb 12, 2026

@copilot apply changes based on the comments in this thread

…n test

1. Extracted createServiceBusJmsConnectionFactory() to a static package-level
   helper method in ServiceBusJmsConnectionFactoryConfiguration to avoid code
   duplication between sender and receiver configurations.

2. Updated integration test to use assertInstanceOf instead of assertSame for
   class type checking, which is more robust for Spring beans that may be proxied.

This prevents future divergence between sender/receiver creation paths and makes
tests less brittle.

Co-authored-by: rujche <171773178+rujche@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 12, 2026

@copilot apply changes based on the comments in this thread

Applied in commit d2cab03:

  1. Code duplication (2796526940): Extracted createServiceBusJmsConnectionFactory() to a static package-level helper method in ServiceBusJmsConnectionFactoryConfiguration class, eliminating duplication between sender and receiver configurations.

  2. Test assertion robustness (2796526956): Changed integration test from assertSame(CachingConnectionFactory.class, connectionFactory.getClass()) to assertInstanceOf(CachingConnectionFactory.class, connectionFactory) to handle proxied beans correctly.

All 77 JMS tests passing, checkstyle 0 violations.

Copy link
Contributor

Copilot AI left a comment

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 6 out of 6 changed files in this pull request and generated no new comments.

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 azure-spring-jms Issues related to Spring JMS. azure-spring-servicebus Spring service bus related issues.

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

[FEATURE REQ] Change sender's default JmsConnectionFactory from ServiceBusJmsConnectionFactory to CachingConnectionFactory

2 participants