Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a76d7f4
Initial plan
Copilot Feb 10, 2026
e4cd102
Change sender's default to CachingConnectionFactory and separate rece…
Copilot Feb 10, 2026
9167051
Update tests to reflect CachingConnectionFactory as default
Copilot Feb 10, 2026
26ce42c
Add CHANGELOG entry for breaking change to default ConnectionFactory
Copilot Feb 10, 2026
2e531a6
Fix receiver to use pooled/cached ConnectionFactory when enabled
Copilot Feb 10, 2026
f1392b1
Fix receiver to use dedicated ConnectionFactory when properties not set
Copilot Feb 10, 2026
29cb8b3
Implement comprehensive JMS ConnectionFactory configuration logic
Copilot Feb 10, 2026
42d8270
Fix checkstyle violations - move && operators to new lines
Copilot Feb 10, 2026
a8679ea
Address code review feedback - improve fallback logic and prevent dup…
Copilot Feb 11, 2026
6ddb625
Simplify receiver ConnectionFactory memoization pattern
Copilot Feb 11, 2026
c3cd994
Add volatile keyword for thread-safe visibility
Copilot Feb 11, 2026
2021086
Use array to make code easier to read
rujche Feb 11, 2026
b622cdf
Update CHANGELOG
rujche Feb 11, 2026
546526a
Fix compile error
rujche Feb 11, 2026
336442f
Fix lint error
rujche Feb 11, 2026
93ee8fa
Fix lifecycle management and add receiver ConnectionFactory tests
Copilot Feb 11, 2026
0b12dc2
Enhance receiver tests to verify actual ConnectionFactory types
Copilot Feb 11, 2026
17ac1c9
Fix test failure
rujche Feb 12, 2026
1deb501
Merge branch 'copilot/change-default-jms-connection-factory' of githu…
rujche Feb 12, 2026
d2cab03
Extract shared helper method and improve type assertion in integratio…
Copilot Feb 12, 2026
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
21 changes: 21 additions & 0 deletions sdk/spring/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@ This section includes changes in `spring-cloud-azure-autoconfigure` module.

- Add ConnectionDetails for ServiceBus. [#44019](https://github.com/Azure/azure-sdk-for-java/pull/44019).

#### Breaking Changes

- Change sender's default JmsConnectionFactory from ServiceBusJmsConnectionFactory to CachingConnectionFactory. [#47923](https://github.com/Azure/azure-sdk-for-java/issues/47923)

The ConnectionFactory type is determined by the following configuration properties:

| `spring.jms.servicebus.pool.enabled` | `spring.jms.cache.enabled` | Sender ConnectionFactory | Receiver ConnectionFactory |
|--------------------------------------|----------------------------|--------------------------------|--------------------------------|
| not set | not set | CachingConnectionFactory | ServiceBusJmsConnectionFactory |
| not set | true | CachingConnectionFactory | CachingConnectionFactory |
| not set | false | ServiceBusJmsConnectionFactory | ServiceBusJmsConnectionFactory |
| true | not set | JmsPoolConnectionFactory | JmsPoolConnectionFactory |
| true | true | CachingConnectionFactory | CachingConnectionFactory |
| true | false | JmsPoolConnectionFactory | JmsPoolConnectionFactory |
| false | not set | CachingConnectionFactory | ServiceBusJmsConnectionFactory |
| false | true | CachingConnectionFactory | CachingConnectionFactory |
| false | false | ServiceBusJmsConnectionFactory | ServiceBusJmsConnectionFactory |

**Note:** `CachingConnectionFactory` and `JmsPoolConnectionFactory` will be used only when they exist in classpath.


### Spring Cloud Azure Docker Compose

This section includes changes in `spring-cloud-azure-docker-compose` module.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.jms.autoconfigure.JmsPoolConnectionFactoryFactory;
import org.springframework.boot.jms.autoconfigure.JmsProperties;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.jms.autoconfigure.JmsPoolConnectionFactoryFactory;
import org.springframework.boot.jms.autoconfigure.JmsProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
Expand All @@ -37,6 +37,58 @@
*/
@Import(ServiceBusJmsConnectionFactoryConfiguration.Registrar.class)
class ServiceBusJmsConnectionFactoryConfiguration {
static final int NOT_CONFIGURED = 0;
static final int TRUE = 1;
static final int FALSE = 2;
static final int POOL = 0;
static final int CACHE = 1;
static final int SERVICE_BUS = 2;

/**
* Creates a ServiceBusJmsConnectionFactory using the provided properties and customizers.
* This is a shared helper method used by both sender and receiver configurations.
*
* @param properties the Azure Service Bus JMS properties
* @param customizers the list of customizers to apply
* @return a configured ServiceBusJmsConnectionFactory instance
*/
static ServiceBusJmsConnectionFactory createServiceBusJmsConnectionFactory(
AzureServiceBusJmsProperties properties,
java.util.List<AzureServiceBusJmsConnectionFactoryCustomizer> customizers) {
return new ServiceBusJmsConnectionFactoryFactory(properties, customizers)
.createConnectionFactory(ServiceBusJmsConnectionFactory.class);
}

/**
* Registers the appropriate ConnectionFactory bean based on configuration properties.
* <p>
* The ConnectionFactory type is determined by the following table:
* <table border="1">
* <tr>
* <th>spring.jms.servicebus.pool.enabled</th>
* <th>spring.jms.cache.enabled</th>
* <th>Sender ConnectionFactory</th>
* </tr>
* <tr><td>not set</td><td>not set</td><td>CachingConnectionFactory</td></tr>
* <tr><td>not set</td><td>true</td><td>CachingConnectionFactory</td></tr>
* <tr><td>not set</td><td>false</td><td>ServiceBusJmsConnectionFactory</td></tr>
* <tr><td>true</td><td>not set</td><td>JmsPoolConnectionFactory</td></tr>
* <tr><td>true</td><td>true</td><td>CachingConnectionFactory</td></tr>
* <tr><td>true</td><td>false</td><td>JmsPoolConnectionFactory</td></tr>
* <tr><td>false</td><td>not set</td><td>CachingConnectionFactory</td></tr>
* <tr><td>false</td><td>true</td><td>CachingConnectionFactory</td></tr>
* <tr><td>false</td><td>false</td><td>ServiceBusJmsConnectionFactory</td></tr>
* </table>
* <p>
*/
private static final int[][] DECISION_TABLE = {
// pool: not set
{CACHE, CACHE, SERVICE_BUS}, // cache: not set, true, false
// pool: true
{POOL, CACHE, POOL}, // cache: not set, true, false
// pool: false
{CACHE, CACHE, SERVICE_BUS} // cache: not set, true, false
};

static class Registrar implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {

Expand All @@ -61,17 +113,44 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
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;
switch (getFactoryType(poolEnabledResult, cacheEnabledResult, DECISION_TABLE)) {
case POOL:
registerJmsPoolConnectionFactory(registry);
break;
case CACHE:
registerJmsCachingConnectionFactory(registry);
break;
default:
registerServiceBusJmsConnectionFactory(registry);
}
}

if (isCacheConnectionFactoryClassPresent() && cacheEnabledResult.orElseGet(() -> false)) {
registerJmsCachingConnectionFactory(registry);
return;
static int getFactoryType(BindResult<Boolean> poolEnabledResult, BindResult<Boolean> cacheEnabledResult, int[][] decisionTable) {
int poolIndex = NOT_CONFIGURED;
if (poolEnabledResult.isBound()) {
poolIndex = poolEnabledResult.get() ? TRUE : FALSE;
}
int cacheIndex = NOT_CONFIGURED;
if (cacheEnabledResult.isBound()) {
cacheIndex = cacheEnabledResult.get() ? TRUE : FALSE;
}
int configuredFactoryType = decisionTable[poolIndex][cacheIndex];
switch (configuredFactoryType) {
case POOL:
if (isPoolConnectionFactoryClassPresent()) {
return POOL;
} else {
return SERVICE_BUS;
}
case CACHE:
if (isCacheConnectionFactoryClassPresent()) {
return CACHE;
} else {
return SERVICE_BUS;
}
default:
return SERVICE_BUS;
}

registerServiceBusJmsConnectionFactory(registry);
}

private static boolean isCacheConnectionFactoryClassPresent() {
Expand Down Expand Up @@ -117,9 +196,9 @@ private void registerJmsPoolConnectionFactory(BeanDefinitionRegistry registry) {
private ServiceBusJmsConnectionFactory createServiceBusJmsConnectionFactory() {
AzureServiceBusJmsProperties serviceBusJmsProperties = beanFactory.getBean(AzureServiceBusJmsProperties.class);
ObjectProvider<AzureServiceBusJmsConnectionFactoryCustomizer> factoryCustomizers = beanFactory.getBeanProvider(AzureServiceBusJmsConnectionFactoryCustomizer.class);
return new ServiceBusJmsConnectionFactoryFactory(serviceBusJmsProperties,
factoryCustomizers.orderedStream().collect(Collectors.toList()))
.createConnectionFactory(ServiceBusJmsConnectionFactory.class);
return ServiceBusJmsConnectionFactoryConfiguration.createServiceBusJmsConnectionFactory(
serviceBusJmsProperties,
factoryCustomizers.orderedStream().collect(Collectors.toList()));
}
}
}
Loading
Loading