Skip to content
Closed
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 @@ -43,6 +43,9 @@
<!-- Suppress LineLength for i18n message properties -->
<suppress checks="LineLength" files="messages.properties"/>

<!-- Suppress RedundantModifier for ServiceBusJmsConnectionFactoryConfigurationTests because public constructors are required for reflection -->
<suppress checks="RedundantModifier" files=".*[/\\]ServiceBusJmsConnectionFactoryConfigurationTests\.java"/>

<!-- OpenTelemetry tracing are plugin packages and shouldn't be referenced -->
<suppress checks="IllegalImport" files=".*[/\\]com[/\\]azure[/\\]spring[/\\]cloud[/\\]autoconfigure[/\\]monitor[/\\]*"/>

Expand Down
2 changes: 2 additions & 0 deletions sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Features Added

- Added `ServiceBusJmsConnectionFactoryClassProvider` interface to allow users to specify a custom subclass of `ServiceBusJmsConnectionFactory` to be used in the configuration. This enables injection of custom connection factory implementations with additional functionality beyond the standard Service Bus JMS connection factory.

### Breaking Changes

### Bugs Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.azure.servicebus.jms.ServiceBusJmsConnectionFactory;
import com.azure.spring.cloud.autoconfigure.implementation.jms.properties.AzureServiceBusJmsProperties;
import com.azure.spring.cloud.autoconfigure.jms.AzureServiceBusJmsConnectionFactoryCustomizer;
import com.azure.spring.cloud.autoconfigure.jms.ServiceBusJmsConnectionFactoryClassProvider;
import jakarta.jms.ConnectionFactory;
import org.messaginghub.pooled.jms.JmsPoolConnectionFactory;
import org.springframework.beans.BeansException;
Expand Down Expand Up @@ -118,9 +119,15 @@ private void registerJmsPoolConnectionFactory(BeanDefinitionRegistry registry) {
private ServiceBusJmsConnectionFactory createServiceBusJmsConnectionFactory() {
AzureServiceBusJmsProperties serviceBusJmsProperties = beanFactory.getBean(AzureServiceBusJmsProperties.class);
ObjectProvider<AzureServiceBusJmsConnectionFactoryCustomizer> factoryCustomizers = beanFactory.getBeanProvider(AzureServiceBusJmsConnectionFactoryCustomizer.class);
ObjectProvider<ServiceBusJmsConnectionFactoryClassProvider> classProvider = beanFactory.getBeanProvider(ServiceBusJmsConnectionFactoryClassProvider.class);

Class<? extends ServiceBusJmsConnectionFactory> factoryClass = classProvider
.getIfAvailable(() -> () -> ServiceBusJmsConnectionFactory.class)
.getConnectionFactoryClass();

return new ServiceBusJmsConnectionFactoryFactory(serviceBusJmsProperties,
factoryCustomizers.orderedStream().collect(Collectors.toList()))
.createConnectionFactory(ServiceBusJmsConnectionFactory.class);
.createConnectionFactory(factoryClass);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.cloud.autoconfigure.jms;

import com.azure.servicebus.jms.ServiceBusJmsConnectionFactory;

/**
* A provider for specifying the desired {@link ServiceBusJmsConnectionFactory} class to be used.
* <p>
* Implement this interface and define it as a bean to inject a custom subclass of ServiceBusJmsConnectionFactory.
* This allows you to use a custom connection factory implementation with additional functionality beyond the
* standard Service Bus JMS connection factory.
* </p>
* <p>
* Example usage:
* <pre>{@code
* @Configuration
* public class CustomJmsConfiguration {
* @Bean
* public ServiceBusJmsConnectionFactoryClassProvider connectionFactoryClassProvider() {
* return () -> CustomServiceBusJmsConnectionFactory.class;
* }
* }
*
* public class CustomServiceBusJmsConnectionFactory extends ServiceBusJmsConnectionFactory {
* public CustomServiceBusJmsConnectionFactory(String connectionString, ServiceBusJmsConnectionFactorySettings settings) {
* super(connectionString, settings);
* // Add custom initialization
* }
*
* public CustomServiceBusJmsConnectionFactory(TokenCredential tokenCredential, String host, ServiceBusJmsConnectionFactorySettings settings) {
* super(tokenCredential, host, settings);
* // Add custom initialization
* }
*
* // Add custom methods or override existing ones
* }
* }</pre>
* </p>
* <p>
* <strong>Requirements:</strong>
* <ul>
* <li>The custom class must extend {@link ServiceBusJmsConnectionFactory}</li>
* <li>The custom class must have a constructor accepting {@code (String, ServiceBusJmsConnectionFactorySettings)}
* for connection string-based authentication</li>
* <li>The custom class must have a constructor accepting {@code (TokenCredential, String, ServiceBusJmsConnectionFactorySettings)}
* for passwordless authentication</li>
* </ul>
* </p>
*
* @since 6.1.0
*/
@FunctionalInterface
public interface ServiceBusJmsConnectionFactoryClassProvider {

/**
* Get the class of the ServiceBusJmsConnectionFactory to be instantiated.
* The class must extend {@link ServiceBusJmsConnectionFactory} and have the required constructors.
*
* @return The class to be used for creating the ServiceBusJmsConnectionFactory instance.
*/
Class<? extends ServiceBusJmsConnectionFactory> getConnectionFactoryClass();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@

package com.azure.spring.cloud.autoconfigure.implementation.jms;

import com.azure.core.credential.TokenCredential;
import com.azure.servicebus.jms.ServiceBusJmsConnectionFactory;
import com.azure.servicebus.jms.ServiceBusJmsConnectionFactorySettings;
import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties;
import com.azure.spring.cloud.autoconfigure.jms.ServiceBusJmsConnectionFactoryClassProvider;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.messaginghub.pooled.jms.JmsPoolConnectionFactory;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jms.connection.CachingConnectionFactory;
Expand Down Expand Up @@ -140,9 +145,80 @@ void useCacheConnectionViaAdditionConfigurationFile(String pricingTier) {
});
}

@Test
void useCustomServiceBusJmsConnectionFactoryClass() {
this.contextRunner
.withUserConfiguration(CustomConnectionFactoryClassConfiguration.class)
.withPropertyValues(
"spring.jms.servicebus.pricing-tier=premium",
"spring.jms.servicebus.pool.enabled=false",
"spring.jms.cache.enabled=false"
)
.run(context -> {
assertThat(context).hasSingleBean(ServiceBusJmsConnectionFactory.class);
ServiceBusJmsConnectionFactory factory = context.getBean(ServiceBusJmsConnectionFactory.class);
assertThat(factory).isInstanceOf(CustomServiceBusJmsConnectionFactory.class);
});
}

@Test
void useCustomServiceBusJmsConnectionFactoryClassWithCaching() {
this.contextRunner
.withUserConfiguration(CustomConnectionFactoryClassConfiguration.class)
.withPropertyValues(
"spring.jms.servicebus.pricing-tier=premium",
"spring.jms.servicebus.pool.enabled=false"
)
.run(context -> {
assertThat(context).hasSingleBean(CachingConnectionFactory.class);
CachingConnectionFactory cachingFactory = context.getBean(CachingConnectionFactory.class);
assertThat(cachingFactory.getTargetConnectionFactory()).isInstanceOf(CustomServiceBusJmsConnectionFactory.class);
});
}

@Test
void useCustomServiceBusJmsConnectionFactoryClassWithPooling() {
this.contextRunner
.withUserConfiguration(CustomConnectionFactoryClassConfiguration.class)
.withPropertyValues(
"spring.jms.servicebus.pricing-tier=premium"
)
.run(context -> {
assertThat(context).hasSingleBean(JmsPoolConnectionFactory.class);
JmsPoolConnectionFactory poolFactory = context.getBean(JmsPoolConnectionFactory.class);
assertThat(poolFactory.getConnectionFactory()).isInstanceOf(CustomServiceBusJmsConnectionFactory.class);
});
}

@Configuration
@PropertySource("classpath:servicebus/additional.properties")
static class AdditionalPropertySourceConfiguration {

}

@Configuration
static class CustomConnectionFactoryClassConfiguration {
@Bean
ServiceBusJmsConnectionFactoryClassProvider serviceBusJmsConnectionFactoryClassProvider() {
return () -> CustomServiceBusJmsConnectionFactory.class;
}
}

/**
* Custom subclass of ServiceBusJmsConnectionFactory for testing.
* Public constructors are required for reflection-based instantiation.
*/
public static class CustomServiceBusJmsConnectionFactory extends ServiceBusJmsConnectionFactory {
public CustomServiceBusJmsConnectionFactory() {
super();
}

public CustomServiceBusJmsConnectionFactory(String connectionString, ServiceBusJmsConnectionFactorySettings settings) {
super(connectionString, settings);
}

public CustomServiceBusJmsConnectionFactory(TokenCredential tokenCredential, String host, ServiceBusJmsConnectionFactorySettings settings) {
super(tokenCredential, host, settings);
}
}
}
Loading