Skip to content

Commit fcb93d4

Browse files
authored
support spring jms configuration for listener (Azure#23531)
1 parent 14359a5 commit fcb93d4

File tree

10 files changed

+383
-192
lines changed

10 files changed

+383
-192
lines changed

sdk/spring/azure-spring-boot-starter-servicebus-jms/CHANGELOG.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@
33
## 3.9.0-beta.1 (Unreleased)
44

55
### Features Added
6-
6+
- Support configuration of `AbstractJmsListenerContainerFactory` for `JmsListener`. Enabled properties include `replyPubSubDomain`, `replyQosSettings`, `subscriptionDurable`, `subscriptionShared` and `phase` with prefix as `spring.jms.servicebus.listener`.
77
### Breaking Changes
88

99
### Bugs Fixed
10-
10+
- Fix the bug of not supporting Spring Boot autoconfiguration of JMS listener.
1111
### Other Changes
1212

1313
## 3.8.0 (2021-08-25)
1414
This release is compatible with Spring Boot 2.5.0 - 2.5.3.
1515
### Dependency Upgrades
1616
- Upgrade to [spring-boot-dependencies:2.5.3](https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/2.5.3/spring-boot-dependencies-2.5.3.pom).
1717

18-
1918
## 3.7.0 (2021-07-20)
2019
### Dependency Upgrades
2120
- Upgrade to [spring-boot-dependencies:2.5.2](https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/2.5.2/spring-boot-dependencies-2.5.2.pom).

sdk/spring/azure-spring-boot-starter-servicebus-jms/README.md

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,30 @@ This starter uses Azure Service Bus messaging features (queues and publish/subsc
2525
The Advanced Message Queuing Protocol (AMQP) 1.0 is an efficient, reliable, wire-level messaging protocol that you can use to build robust, cross-platform messaging applications.
2626

2727
Support for AMQP 1.0 in Azure Service Bus means that you can use the queuing and publish/subscribe brokered messaging features from a range of platforms using an efficient binary protocol. Furthermore, you can build applications comprised of components built using a mix of languages, frameworks, and operating systems.
28+
29+
### Support for JmsListener
30+
azure-spring-boot-starter-servicebus-jms autoconfigures two Spring beans of `JmsListenerContainerFactory` for Azure Service Bus Queue and Topic, with the bean name as `jmsListenerContainerFactory` and `topicJmsListenerContainerFactory`.
31+
2832
## Examples
2933

3034
### Configure the app for your service bus
3135
In this section, you see how to configure your app to use either a Service Bus queue or topic.
3236

3337
#### configuration options
3438

35-
Name | Description
36-
|:---|:---
37-
spring.jms.servicebus.connection-string | Specify the connection string you obtained in your Service Bus namespace from the Azure portal.
38-
spring.jms.servicebus.topic-client-id | Specify the JMS client ID, which is your Service Bus Subscription name in the Azure portal.
39-
spring.jms.servicebus.idle-timeout | Specify the idle timeout in milliseconds. The recommended value for this tutorial is 1800000.
40-
spring.jms.servicebus.pricing-tier | Specify the pricing tier of your service bus. Supported values are *premium*, *standard*, and *basic*. Premium uses Java Message Service (JMS) 2.0, while standard and basic use JMS 1.0 to interact with Azure Service Bus.
41-
42-
#### Use a Service Bus queue
39+
Name | Description | Type |Required
40+
|:---|:---|:---|:---
41+
spring.jms.servicebus.connection-string | Connection string of your Service Bus namespace from the Azure portal.|String | Yes
42+
spring.jms.servicebus.topic-client-id | JMS client ID, which is your Service Bus Subscription name in the Azure portal.|String | Yes if using Service Bus Topic.
43+
spring.jms.servicebus.idle-timeout | Idle timeout in milliseconds. The recommended value for this tutorial is 1800000.| int | Yes
44+
spring.jms.servicebus.pricing-tier | Pricing tier of your Service Bus namespace. Supported values are *premium*, *standard*, and *basic*. Premium uses Java Message Service (JMS) 2.0, while standard and basic use JMS 1.0 to interact with Azure Service Bus.|String | Yes
45+
spring.jms.servicebus.listener.reply-pub-sub-domain| Whether the reply destination type is topic. Only works for the bean of topicJmsListenerContainerFactory.| Boolean |
46+
spring.jms.servicebus.listener.reply-qos-settings.*|Configure the QosSettings to use when sending a reply. Can be set to null to indicate that the broker's defaults should be used. |
47+
spring.jms.servicebus.listener.subscription-durable|Whether to make the subscription durable. Only works for the bean of topicJmsListenerContainerFactory. The default value is true.|Boolean|
48+
spring.jms.servicebus.listener.subscription-durable|Whether to make the subscription shared. Only works for the bean of topicJmsListenerContainerFactory.|Boolean|
49+
spring.jms.servicebus.listener.phase|Specify the phase in which this container should be started and stopped.|Integer|
4350

44-
Append the following code to the end of the *application.properties* file. Replace the sample values with the appropriate values for your service bus:
45-
46-
```yaml
47-
spring.jms.servicebus.connection-string=<ServiceBusNamespaceConnectionString>
48-
spring.jms.servicebus.idle-timeout=<IdleTimeout>
49-
spring.jms.servicebus.pricing-tier=<ServiceBusPricingTier>
50-
```
51-
52-
#### Use Service Bus topic
53-
54-
Append the following code to the end of the *application.properties* file. Replace the sample values with the appropriate values for your service bus:
55-
56-
```yaml
57-
spring.jms.servicebus.connection-string=<ServiceBusNamespaceConnectionString>
58-
spring.jms.servicebus.topic-client-id=<ServiceBusTopicClientId>
59-
spring.jms.servicebus.idle-timeout=<IdleTimeout>
60-
spring.jms.servicebus.pricing-tier=<ServiceBusPricingTier>
61-
```
51+
Note: `JmsListenerContainerFactory` beans also support all [Spring native application properties of JmsListener][Spring_jms_configuration].
6252

6353
### Implement basic Service Bus functionality
6454

@@ -271,3 +261,4 @@ Please follow [instructions here](https://github.com/Azure/azure-sdk-for-java/bl
271261
[spring_jms_guide]: https://spring.io/guides/gs/messaging-jms/
272262
[environment_checklist]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/spring/ENVIRONMENT_CHECKLIST.md#ready-to-run-checklist
273263
[Add azure-spring-boot-bom]: https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/spring/AZURE_SPRING_BOMS_USAGE.md#add-azure-spring-boot-bom
264+
[Spring_jms_configuration]: https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties.integration.spring.jms.listener.acknowledge-mode

sdk/spring/azure-spring-boot/CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## 3.9.0-beta.1 (Unreleased)
44

55
### Features Added
6-
6+
- Support configuration of `AbstractJmsListenerContainerFactory` for `JmsListener`. Enabled properties include `replyPubSubDomain`, `replyQosSettings`, `subscriptionDurable`, `subscriptionShared` and `phase` with prefix as `spring.jms.servicebus.listener`.
77
### Breaking Changes
88

99
### Bugs Fixed
@@ -16,7 +16,7 @@
1616
| Yes | No | `web_application` | `web_application` |
1717
| No | Yes | `resource_server` | `resource_server` |
1818
| Yes | Yes | `web_application`,`resource_server`,`resource_server_with_obo`, `web_application_and_resource_server` | `resource_server_with_obo` |
19-
19+
- Fix the bug of not supporting Spring Boot autoconfiguration of JMS listener.
2020
### Deprecations
2121
- Deprecate `azure.activedirectory.authorization-clients.xxx.on-demand`, use `azure.activedirectory.authorization-clients.xxx.authorization-grant-type` instead. If you are using `on-demand=true`, please change to `authorization-grant-type=authorization_code`.
2222

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.azure.spring.autoconfigure.jms;
5+
6+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
7+
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
10+
import org.springframework.jms.config.JmsListenerContainerFactory;
11+
12+
import javax.jms.ConnectionFactory;
13+
14+
/**
15+
* Abstract autoconfiguration class of ServiceBusJMS for JmsListenerContainerFactory.
16+
*/
17+
public abstract class AbstractServiceBusJMSAutoConfiguration {
18+
19+
protected final AzureServiceBusJMSProperties azureServiceBusJMSProperties;
20+
21+
public AbstractServiceBusJMSAutoConfiguration(AzureServiceBusJMSProperties azureServiceBusJMSProperties) {
22+
this.azureServiceBusJMSProperties = azureServiceBusJMSProperties;
23+
}
24+
25+
/**
26+
* Declare {@link JmsListenerContainerFactory} bean for Azure Service Bus Queue.
27+
* @param configurer configure {@link DefaultJmsListenerContainerFactory} with sensible defaults
28+
* @param connectionFactory configure {@link ConnectionFactory} for {@link JmsListenerContainerFactory}
29+
* @return {@link JmsListenerContainerFactory} bean
30+
*/
31+
@Bean
32+
@ConditionalOnMissingBean
33+
public JmsListenerContainerFactory<?> jmsListenerContainerFactory(
34+
DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
35+
DefaultJmsListenerContainerFactory jmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
36+
configurer.configure(jmsListenerContainerFactory, connectionFactory);
37+
jmsListenerContainerFactory.setPubSubDomain(Boolean.FALSE);
38+
configureCommonListenerContainerFactory(jmsListenerContainerFactory);
39+
return jmsListenerContainerFactory;
40+
}
41+
42+
/**
43+
* Declare {@link JmsListenerContainerFactory} bean for Azure Service Bus Topic.
44+
* @param configurer configure {@link DefaultJmsListenerContainerFactory} with sensible defaults
45+
* @param connectionFactory configure {@link ConnectionFactory} for {@link JmsListenerContainerFactory}
46+
* @return {@link JmsListenerContainerFactory} bean
47+
*/
48+
@Bean
49+
@ConditionalOnMissingBean(name = "topicJmsListenerContainerFactory")
50+
public JmsListenerContainerFactory<?> topicJmsListenerContainerFactory(
51+
DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
52+
DefaultJmsListenerContainerFactory jmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
53+
configurer.configure(jmsListenerContainerFactory, connectionFactory);
54+
jmsListenerContainerFactory.setPubSubDomain(Boolean.TRUE);
55+
configureCommonListenerContainerFactory(jmsListenerContainerFactory);
56+
configureTopicListenerContainerFactory(jmsListenerContainerFactory);
57+
return jmsListenerContainerFactory;
58+
}
59+
60+
private void configureCommonListenerContainerFactory(DefaultJmsListenerContainerFactory jmsListenerContainerFactory) {
61+
AzureServiceBusJMSProperties.Listener listener = azureServiceBusJMSProperties.getListener();
62+
if (listener.getReplyQosSettings() != null) {
63+
jmsListenerContainerFactory.setReplyQosSettings(listener.getReplyQosSettings());
64+
}
65+
if (listener.getPhase() != null) {
66+
jmsListenerContainerFactory.setPhase(listener.getPhase());
67+
}
68+
}
69+
70+
private void configureTopicListenerContainerFactory(DefaultJmsListenerContainerFactory jmsListenerContainerFactory) {
71+
AzureServiceBusJMSProperties.Listener listener = azureServiceBusJMSProperties.getListener();
72+
if (azureServiceBusJMSProperties.getTopicClientId() != null) {
73+
jmsListenerContainerFactory.setClientId(azureServiceBusJMSProperties.getTopicClientId());
74+
}
75+
if (listener.isReplyPubSubDomain() != null) {
76+
jmsListenerContainerFactory.setReplyPubSubDomain(listener.isReplyPubSubDomain());
77+
}
78+
if (listener.isSubscriptionDurable() != null) {
79+
jmsListenerContainerFactory.setSubscriptionDurable(listener.isSubscriptionDurable());
80+
}
81+
if (listener.isSubscriptionShared() != null) {
82+
jmsListenerContainerFactory.setSubscriptionShared(listener.isSubscriptionShared());
83+
}
84+
}
85+
}

sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/jms/AzureServiceBusJMSProperties.java

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
77
import org.springframework.boot.context.properties.ConfigurationProperties;
8+
import org.springframework.jms.support.QosSettings;
89
import org.springframework.util.StringUtils;
910
import org.springframework.validation.annotation.Validated;
1011

@@ -20,14 +21,16 @@ public class AzureServiceBusJMSProperties {
2021
private String connectionString;
2122

2223
/**
23-
* JMS clientID
24+
* JMS clientID. Only works for the bean of topicJmsListenerContainerFactory.
2425
*/
2526
private String topicClientId;
2627

2728
private int idleTimeout = 1800000;
2829

2930
private String pricingTier;
3031

32+
private final Listener listener = new Listener();
33+
3134
public String getConnectionString() {
3235
return connectionString;
3336
}
@@ -60,6 +63,10 @@ public void setIdleTimeout(int idleTimeout) {
6063
this.idleTimeout = idleTimeout;
6164
}
6265

66+
public Listener getListener() {
67+
return listener;
68+
}
69+
6370
/**
6471
* Validate spring.jms.servicebus related properties.
6572
*
@@ -76,4 +83,77 @@ public void validate() {
7683
throw new IllegalArgumentException("'spring.jms.servicebus.pricing-tier' is not valid");
7784
}
7885
}
86+
87+
/**
88+
* Properties to configure {@link org.springframework.jms.annotation.JmsListener} for
89+
* {@link org.springframework.jms.config.AbstractJmsListenerContainerFactory}.
90+
*/
91+
public static class Listener {
92+
93+
/**
94+
* Whether the reply destination type is topic. Only works for the bean of topicJmsListenerContainerFactory.
95+
*/
96+
private Boolean replyPubSubDomain;
97+
98+
/**
99+
* Configure the {@link QosSettings} to use when sending a reply.
100+
*/
101+
private QosSettings replyQosSettings;
102+
103+
/**
104+
* Whether to make the subscription durable. Only works for the bean of topicJmsListenerContainerFactory.
105+
*/
106+
private Boolean subscriptionDurable = Boolean.TRUE;
107+
108+
/**
109+
* Whether to make the subscription shared. Only works for the bean of topicJmsListenerContainerFactory.
110+
*/
111+
private Boolean subscriptionShared;
112+
113+
/**
114+
* Specify the phase in which this container should be started and
115+
* stopped.
116+
*/
117+
private Integer phase;
118+
119+
public Boolean isReplyPubSubDomain() {
120+
return replyPubSubDomain;
121+
}
122+
123+
public void setReplyPubSubDomain(Boolean replyPubSubDomain) {
124+
this.replyPubSubDomain = replyPubSubDomain;
125+
}
126+
127+
public QosSettings getReplyQosSettings() {
128+
return replyQosSettings;
129+
}
130+
131+
public void setReplyQosSettings(QosSettings replyQosSettings) {
132+
this.replyQosSettings = replyQosSettings;
133+
}
134+
135+
public Boolean isSubscriptionDurable() {
136+
return subscriptionDurable;
137+
}
138+
139+
public void setSubscriptionDurable(Boolean subscriptionDurable) {
140+
this.subscriptionDurable = subscriptionDurable;
141+
}
142+
143+
public Boolean isSubscriptionShared() {
144+
return subscriptionShared;
145+
}
146+
147+
public void setSubscriptionShared(Boolean subscriptionShared) {
148+
this.subscriptionShared = subscriptionShared;
149+
}
150+
151+
public Integer getPhase() {
152+
return phase;
153+
}
154+
155+
public void setPhase(Integer phase) {
156+
this.phase = phase;
157+
}
158+
}
79159
}

sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/autoconfigure/jms/NonPremiumServiceBusJMSAutoConfiguration.java

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
import org.springframework.boot.context.properties.EnableConfigurationProperties;
1313
import org.springframework.context.annotation.Bean;
1414
import org.springframework.context.annotation.Configuration;
15-
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
16-
import org.springframework.jms.config.JmsListenerContainerFactory;
1715

1816
import javax.jms.ConnectionFactory;
1917

@@ -26,45 +24,33 @@
2624
@ConditionalOnProperty(value = "spring.jms.servicebus.enabled", matchIfMissing = true)
2725
@ConditionalOnExpression(value = "not '${spring.jms.servicebus.pricing-tier}'.equalsIgnoreCase('premium')")
2826
@EnableConfigurationProperties(AzureServiceBusJMSProperties.class)
29-
public class NonPremiumServiceBusJMSAutoConfiguration {
27+
public class NonPremiumServiceBusJMSAutoConfiguration extends AbstractServiceBusJMSAutoConfiguration {
3028

3129
private static final String AMQP_URI_FORMAT = "amqps://%s?amqp.idleTimeout=%d";
3230

31+
public NonPremiumServiceBusJMSAutoConfiguration(AzureServiceBusJMSProperties azureServiceBusJMSProperties) {
32+
super(azureServiceBusJMSProperties);
33+
}
34+
3335
@Bean
3436
@ConditionalOnMissingBean
35-
public ConnectionFactory jmsConnectionFactory(AzureServiceBusJMSProperties serviceBusJMSProperties) {
36-
final String connectionString = serviceBusJMSProperties.getConnectionString();
37-
final String clientId = serviceBusJMSProperties.getTopicClientId();
38-
final int idleTimeout = serviceBusJMSProperties.getIdleTimeout();
39-
40-
final ServiceBusKey serviceBusKey = ConnectionStringResolver.getServiceBusKey(connectionString);
41-
final String host = serviceBusKey.getHost();
42-
final String sasKeyName = serviceBusKey.getSharedAccessKeyName();
43-
final String sasKey = serviceBusKey.getSharedAccessKey();
44-
45-
final String remoteUri = String.format(AMQP_URI_FORMAT, host, idleTimeout);
46-
final JmsConnectionFactory jmsConnectionFactory = new JmsConnectionFactory();
37+
public ConnectionFactory jmsConnectionFactory() {
38+
String connectionString = azureServiceBusJMSProperties.getConnectionString();
39+
String clientId = azureServiceBusJMSProperties.getTopicClientId();
40+
int idleTimeout = azureServiceBusJMSProperties.getIdleTimeout();
41+
42+
ServiceBusKey serviceBusKey = ConnectionStringResolver.getServiceBusKey(connectionString);
43+
String host = serviceBusKey.getHost();
44+
String sasKeyName = serviceBusKey.getSharedAccessKeyName();
45+
String sasKey = serviceBusKey.getSharedAccessKey();
46+
47+
String remoteUri = String.format(AMQP_URI_FORMAT, host, idleTimeout);
48+
JmsConnectionFactory jmsConnectionFactory = new JmsConnectionFactory();
4749
jmsConnectionFactory.setRemoteURI(remoteUri);
4850
jmsConnectionFactory.setClientID(clientId);
4951
jmsConnectionFactory.setUsername(sasKeyName);
5052
jmsConnectionFactory.setPassword(sasKey);
5153
return jmsConnectionFactory;
5254
}
5355

54-
@Bean
55-
@ConditionalOnMissingBean
56-
public JmsListenerContainerFactory<?> jmsListenerContainerFactory(ConnectionFactory connectionFactory) {
57-
final DefaultJmsListenerContainerFactory jmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
58-
jmsListenerContainerFactory.setConnectionFactory(connectionFactory);
59-
return jmsListenerContainerFactory;
60-
}
61-
62-
@Bean
63-
public JmsListenerContainerFactory<?> topicJmsListenerContainerFactory(ConnectionFactory connectionFactory) {
64-
final DefaultJmsListenerContainerFactory jmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
65-
jmsListenerContainerFactory.setConnectionFactory(connectionFactory);
66-
jmsListenerContainerFactory.setSubscriptionDurable(Boolean.TRUE);
67-
return jmsListenerContainerFactory;
68-
}
69-
7056
}

0 commit comments

Comments
 (0)