Skip to content

Commit f20192b

Browse files
authored
Add unit test for CachingConnectionFactory producer/consumer reuse and update azure-servicebus-jms to 2.1.0 (#47282)
1 parent 684d009 commit f20192b

File tree

4 files changed

+107
-4
lines changed

4 files changed

+107
-4
lines changed

eng/versioning/external_dependencies.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# Format;
99
# groupId:artifactId;dependency-version
1010
ch.qos.logback:logback-classic;1.3.14
11-
com.azure:azure-servicebus-jms;2.0.0
11+
com.azure:azure-servicebus-jms;2.1.0
1212
com.azure.tools:azure-autorest-customization;1.0.0-beta.11
1313
com.fasterxml:aalto-xml;1.3.3
1414
com.fasterxml.jackson.core:jackson-annotations;2.18.4

sdk/spring/spring-cloud-azure-autoconfigure/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@
140140
<dependency>
141141
<groupId>com.azure</groupId>
142142
<artifactId>azure-servicebus-jms</artifactId>
143-
<version>2.0.0</version> <!-- {x-version-update;com.azure:azure-servicebus-jms;external_dependency} -->
143+
<version>2.1.0</version> <!-- {x-version-update;com.azure:azure-servicebus-jms;external_dependency} -->
144144
<optional>true</optional>
145145
<exclusions>
146146
<exclusion>
@@ -542,7 +542,7 @@
542542
<rules>
543543
<bannedDependencies>
544544
<includes>
545-
<include>com.azure:azure-servicebus-jms:[2.0.0]</include> <!-- {x-include-update;com.azure:azure-servicebus-jms;external_dependency} -->
545+
<include>com.azure:azure-servicebus-jms:[2.1.0]</include> <!-- {x-include-update;com.azure:azure-servicebus-jms;external_dependency} -->
546546
<include>com.mysql:mysql-connector-j:[9.4.0]</include> <!-- {x-include-update;springboot3_com.mysql:mysql-connector-j;external_dependency} -->
547547
<include>com.fasterxml.jackson.core:jackson-annotations:[2.19.2]</include> <!-- {x-include-update;springboot3_com.fasterxml.jackson.core:jackson-annotations;external_dependency} -->
548548
<include>com.fasterxml.jackson.core:jackson-core:[2.19.2]</include> <!-- {x-include-update;springboot3_com.fasterxml.jackson.core:jackson-core;external_dependency} -->

sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsConnectionFactoryConfigurationTests.java

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55

66
import com.azure.servicebus.jms.ServiceBusJmsConnectionFactory;
77
import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties;
8+
import jakarta.jms.Connection;
9+
import jakarta.jms.ConnectionFactory;
10+
import jakarta.jms.Destination;
11+
import jakarta.jms.MessageProducer;
12+
import jakarta.jms.Queue;
13+
import jakarta.jms.Session;
14+
import org.junit.jupiter.api.Test;
815
import org.junit.jupiter.params.ParameterizedTest;
916
import org.junit.jupiter.params.provider.ValueSource;
1017
import org.messaginghub.pooled.jms.JmsPoolConnectionFactory;
@@ -18,6 +25,11 @@
1825

1926
import static com.azure.spring.cloud.autoconfigure.implementation.util.TestServiceBusUtils.CONNECTION_STRING_FORMAT;
2027
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.mockito.ArgumentMatchers.any;
29+
import static org.mockito.ArgumentMatchers.anyBoolean;
30+
import static org.mockito.ArgumentMatchers.anyInt;
31+
import static org.mockito.Mockito.mock;
32+
import static org.mockito.Mockito.when;
2133

2234
class ServiceBusJmsConnectionFactoryConfigurationTests {
2335

@@ -140,6 +152,97 @@ void useCacheConnectionViaAdditionConfigurationFile(String pricingTier) {
140152
});
141153
}
142154

155+
@Test
156+
void cachingConnectionFactoryReusesSameProducerForSameDestination() throws Exception {
157+
// Create mock objects for JMS components
158+
ConnectionFactory mockTargetConnectionFactory = mock(ConnectionFactory.class);
159+
Connection mockConnection = mock(Connection.class);
160+
// Create a mock inner session that ServiceBusJmsSession will wrap
161+
Session mockInnerSession = mock(Session.class);
162+
// Create mock inner queues that the inner session returns
163+
Queue mockInnerQueue1 = mock(Queue.class);
164+
Queue mockInnerQueue2 = mock(Queue.class);
165+
Queue mockInnerQueue3 = mock(Queue.class);
166+
when(mockInnerQueue1.getQueueName()).thenReturn("queue1");
167+
when(mockInnerQueue2.getQueueName()).thenReturn("queue1");
168+
when(mockInnerQueue3.getQueueName()).thenReturn("queue2");
169+
when(mockInnerSession.createQueue("queue1"))
170+
.thenReturn(mockInnerQueue1)
171+
.thenReturn(mockInnerQueue2)
172+
.thenReturn(mockInnerQueue1)
173+
.thenReturn(mockInnerQueue2);
174+
when(mockInnerSession.createQueue("queue2"))
175+
.thenReturn(mockInnerQueue3)
176+
.thenReturn(mockInnerQueue3);
177+
178+
// Create ServiceBusJmsSession using reflection (constructor is package-private)
179+
// ServiceBusJmsSession.createQueue() wraps the inner Queue in ServiceBusJmsQueue
180+
// which has the proper toString() implementation in azure-servicebus-jms 2.1.0
181+
Session serviceBusJmsSession = createServiceBusJmsSession(mockInnerSession);
182+
183+
// Create mock producers - each call to createProducer returns a new unique producer
184+
MessageProducer mockProducer1 = mock(MessageProducer.class);
185+
MessageProducer mockProducer2 = mock(MessageProducer.class);
186+
MessageProducer mockProducer3 = mock(MessageProducer.class);
187+
188+
// Setup mock behavior for connection and session
189+
when(mockTargetConnectionFactory.createConnection()).thenReturn(mockConnection);
190+
when(mockConnection.createSession(anyBoolean(), anyInt())).thenReturn(serviceBusJmsSession);
191+
// Mock createProducer to return different producers for each call
192+
// This simulates that each ServiceBusJmsQueue instance would get a different producer
193+
// if caching doesn't work (i.e., toString() returns unique values as in 2.0.0)
194+
when(mockInnerSession.createProducer(any(Destination.class)))
195+
.thenReturn(mockProducer1)
196+
.thenReturn(mockProducer2)
197+
.thenReturn(mockProducer3);
198+
199+
// Create CachingConnectionFactory with caching enabled
200+
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(mockTargetConnectionFactory);
201+
cachingConnectionFactory.setCacheProducers(true);
202+
203+
// Get connection and session
204+
Connection connection = cachingConnectionFactory.createConnection();
205+
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
206+
207+
// Create queues - these are ServiceBusJmsQueue instances
208+
Queue queue1FirstCall = session.createQueue("queue1");
209+
Queue queue1SecondCall = session.createQueue("queue1");
210+
Queue queue2 = session.createQueue("queue2");
211+
212+
// First call: create producer for queue1 (first ServiceBusJmsQueue instance)
213+
MessageProducer producer1ForQueue1 = session.createProducer(queue1FirstCall);
214+
// Second call: create producer for queue1 (second ServiceBusJmsQueue instance)
215+
// In azure-servicebus-jms 2.1.0, this returns cached producer because toString() is consistent
216+
// In azure-servicebus-jms 2.0.0, this would return a different producer because toString() was unique
217+
MessageProducer producer2ForQueue1 = session.createProducer(queue1SecondCall);
218+
// Third call: create producer for different queue2 - should return different producer
219+
MessageProducer producerForQueue2 = session.createProducer(queue2);
220+
221+
// Verify: same producer is returned for same queue name
222+
// This assertion would fail with azure-servicebus-jms 2.0.0 because toString() returned unique values
223+
assertThat(producer1ForQueue1.toString())
224+
.as("Same producer should be returned for ServiceBusJmsQueue instances with same queue name")
225+
.isEqualTo(producer2ForQueue1.toString());
226+
227+
// Verify: different producer is returned for different queue name
228+
assertThat(producer1ForQueue1.toString())
229+
.as("Different producer should be returned for different queue name")
230+
.isNotEqualTo(producerForQueue2.toString());
231+
232+
// Cleanup
233+
connection.close();
234+
}
235+
236+
/**
237+
* Creates a ServiceBusJmsSession instance using reflection since the constructor is package-private.
238+
*/
239+
private Session createServiceBusJmsSession(Session innerSession) throws Exception {
240+
Class<?> sessionClass = Class.forName("com.azure.servicebus.jms.ServiceBusJmsSession");
241+
java.lang.reflect.Constructor<?> constructor = sessionClass.getDeclaredConstructor(Session.class);
242+
constructor.setAccessible(true);
243+
return (Session) constructor.newInstance(innerSession);
244+
}
245+
143246
@Configuration
144247
@PropertySource("classpath:servicebus/additional.properties")
145248
static class AdditionalPropertySourceConfiguration {

sdk/spring/spring-cloud-azure-starter-servicebus-jms/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
<dependency>
107107
<groupId>com.azure</groupId>
108108
<artifactId>azure-servicebus-jms</artifactId>
109-
<version>2.0.0</version> <!-- {x-version-update;com.azure:azure-servicebus-jms;external_dependency} -->
109+
<version>2.1.0</version> <!-- {x-version-update;com.azure:azure-servicebus-jms;external_dependency} -->
110110
<exclusions>
111111
<exclusion>
112112
<groupId>com.azure</groupId>

0 commit comments

Comments
 (0)