Skip to content

Commit a9100c4

Browse files
committed
Revision of JMS annotated endpoint support, plus support for JMS 2.0's shared subscriptions
Issue: SPR-9882 Issue: SPR-11969
1 parent 3e5946d commit a9100c4

File tree

39 files changed

+863
-776
lines changed

39 files changed

+863
-776
lines changed

spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
* Enable JMS listener annotated endpoints that are created under the cover
2929
* by a {@link org.springframework.jms.config.JmsListenerContainerFactory
3030
* JmsListenerContainerFactory}. To be used on
31-
* @{@link org.springframework.context.annotation.Configuration Configuration} classes
32-
* as follows:
31+
* {@link org.springframework.context.annotation.Configuration Configuration}
32+
* classes as follows:
3333
*
3434
* <pre class="code">
3535
* &#064;Configuration
@@ -52,8 +52,8 @@
5252
* used in the sample above, provides the necessary configuration options that are supported by
5353
* the underlying {@link org.springframework.jms.listener.MessageListenerContainer MessageListenerContainer}.
5454
*
55-
* <p>{@code @EnableJms} enables detection of @{@link JmsListener} annotations on
56-
* any Spring-managed bean in the container. For example, given a class {@code MyService}
55+
* <p>{@code @EnableJms} enables detection of {@link JmsListener} annotations on any
56+
* Spring-managed bean in the container. For example, given a class {@code MyService}:
5757
*
5858
* <pre class="code">
5959
* package com.acme.foo;
@@ -103,7 +103,7 @@
103103
*
104104
* <p>Annotated methods can use flexible signature; in particular, it is possible to use
105105
* the {@link org.springframework.messaging.Message Message} abstraction and related annotations,
106-
* see @{@link JmsListener} Javadoc for more details. For instance, the following would
106+
* see {@link JmsListener} Javadoc for more details. For instance, the following would
107107
* inject the content of the message and a a custom "myCounter" JMS header:
108108
*
109109
* <pre class="code">
@@ -163,7 +163,7 @@
163163
* are created and managed. The example below also demonstrates how to customize the
164164
* {@code JmsHandlerMethodFactory} to use with a custom {@link org.springframework.validation.Validator
165165
* Validator} so that payloads annotated with {@link org.springframework.validation.annotation.Validated
166-
* @Validated} are first validated against a custom {@code Validator}.
166+
* Validated} are first validated against a custom {@code Validator}.
167167
*
168168
* <pre class="code">
169169
* &#064;Configuration

spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
/**
8080
* The unique identifier of the container managing this endpoint.
8181
* <p>if none is specified an auto-generated one is provided.
82-
* @see org.springframework.jms.config.JmsListenerEndpointRegistry#getContainer(String)
82+
* @see org.springframework.jms.config.JmsListenerEndpointRegistry#getListenerContainer(String)
8383
*/
8484
String id() default "";
8585

spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,21 @@
4343
import org.springframework.util.StringUtils;
4444

4545
/**
46-
* Bean post-processor that registers methods annotated with @{@link JmsListener}
46+
* Bean post-processor that registers methods annotated with {@link JmsListener}
4747
* to be invoked by a JMS message listener container created under the cover
4848
* by a {@link org.springframework.jms.config.JmsListenerContainerFactory} according
4949
* to the parameters of the annotation.
5050
*
51-
* <p>Annotated methods can use flexible arguments as defined by @{@link JmsListener}.
51+
* <p>Annotated methods can use flexible arguments as defined by {@link JmsListener}.
5252
*
5353
* <p>This post-processor is automatically registered by Spring's
54-
* {@code <jms:annotation-driven>} XML element, and also by the @{@link EnableJms}
54+
* {@code <jms:annotation-driven>} XML element, and also by the {@link EnableJms}
5555
* annotation.
5656
*
5757
* <p>Auto-detect any {@link JmsListenerConfigurer} instances in the container,
5858
* allowing for customization of the registry to be used, the default container
5959
* factory or for fine-grained control over endpoints registration. See
60-
* @{@link EnableJms} Javadoc for complete usage details.
60+
* {@link EnableJms} Javadoc for complete usage details.
6161
*
6262
* @author Stephane Nicoll
6363
* @since 4.1
@@ -68,7 +68,6 @@
6868
* @see JmsListenerEndpointRegistry
6969
* @see org.springframework.jms.config.AbstractJmsListenerEndpoint
7070
* @see MethodJmsListenerEndpoint
71-
* @see MessageListenerFactory
7271
*/
7372
public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered,
7473
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
@@ -78,21 +77,23 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
7877
*/
7978
static final String DEFAULT_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME = "jmsListenerContainerFactory";
8079

81-
private final AtomicInteger counter = new AtomicInteger();
82-
83-
private ApplicationContext applicationContext;
8480

8581
private JmsListenerEndpointRegistry endpointRegistry;
8682

8783
private String containerFactoryBeanName = DEFAULT_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME;
8884

8985
private final JmsHandlerMethodFactoryAdapter jmsHandlerMethodFactory = new JmsHandlerMethodFactoryAdapter();
9086

87+
private ApplicationContext applicationContext;
88+
9189
private final JmsListenerEndpointRegistrar registrar = new JmsListenerEndpointRegistrar();
9290

91+
private final AtomicInteger counter = new AtomicInteger();
92+
93+
9394
@Override
94-
public void setApplicationContext(ApplicationContext applicationContext) {
95-
this.applicationContext = applicationContext;
95+
public int getOrder() {
96+
return LOWEST_PRECEDENCE;
9697
}
9798

9899
/**
@@ -105,8 +106,7 @@ public void setEndpointRegistry(JmsListenerEndpointRegistry endpointRegistry) {
105106

106107
/**
107108
* Set the name of the {@link JmsListenerContainerFactory} to use by default.
108-
* <p/>If none is specified, {@value #DEFAULT_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME}
109-
* is assumed to be defined.
109+
* <p>If none is specified, "jmsListenerContainerFactory" is assumed to be defined.
110110
*/
111111
public void setContainerFactoryBeanName(String containerFactoryBeanName) {
112112
this.containerFactoryBeanName = containerFactoryBeanName;
@@ -125,10 +125,11 @@ public void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodF
125125
}
126126

127127
@Override
128-
public int getOrder() {
129-
return LOWEST_PRECEDENCE;
128+
public void setApplicationContext(ApplicationContext applicationContext) {
129+
this.applicationContext = applicationContext;
130130
}
131131

132+
132133
@Override
133134
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
134135
return bean;
@@ -162,17 +163,17 @@ protected void processJmsListener(JmsListener jmsListener, Method method, Object
162163
catch (NoSuchMethodException ex) {
163164
throw new IllegalStateException(String.format(
164165
"@JmsListener method '%s' found on bean target class '%s', " +
165-
"but not found in any interface(s) for bean JDK proxy. Either " +
166-
"pull the method up to an interface or switch to subclass (CGLIB) " +
167-
"proxies by setting proxy-target-class/proxyTargetClass " +
168-
"attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName()));
166+
"but not found in any interface(s) for bean JDK proxy. Either " +
167+
"pull the method up to an interface or switch to subclass (CGLIB) " +
168+
"proxies by setting proxy-target-class/proxyTargetClass " +
169+
"attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName()));
169170
}
170171
}
171172

172173
MethodJmsListenerEndpoint endpoint = new MethodJmsListenerEndpoint();
173174
endpoint.setBean(bean);
174175
endpoint.setMethod(method);
175-
endpoint.setJmsHandlerMethodFactory(jmsHandlerMethodFactory);
176+
endpoint.setJmsHandlerMethodFactory(this.jmsHandlerMethodFactory);
176177
endpoint.setId(getEndpointId(jmsListener));
177178
endpoint.setDestination(jmsListener.destination());
178179
if (StringUtils.hasText(jmsListener.selector())) {
@@ -189,12 +190,12 @@ protected void processJmsListener(JmsListener jmsListener, Method method, Object
189190
String containerFactoryBeanName = jmsListener.containerFactory();
190191
if (StringUtils.hasText(containerFactoryBeanName)) {
191192
try {
192-
factory = applicationContext.getBean(containerFactoryBeanName, JmsListenerContainerFactory.class);
193+
factory = this.applicationContext.getBean(containerFactoryBeanName, JmsListenerContainerFactory.class);
193194
}
194-
catch (NoSuchBeanDefinitionException e) {
195-
throw new BeanInitializationException("Could not register jms listener endpoint on ["
196-
+ method + "], no " + JmsListenerContainerFactory.class.getSimpleName() + " with id '"
197-
+ containerFactoryBeanName + "' was found in the application context", e);
195+
catch (NoSuchBeanDefinitionException ex) {
196+
throw new BeanInitializationException("Could not register jms listener endpoint on [" +
197+
method + "], no " + JmsListenerContainerFactory.class.getSimpleName() + " with id '" +
198+
containerFactoryBeanName + "' was found in the application context", ex);
198199
}
199200
}
200201

@@ -214,22 +215,20 @@ public void onApplicationEvent(ContextRefreshedEvent event) {
214215
configurer.configureJmsListeners(registrar);
215216
}
216217

217-
registrar.setApplicationContext(this.applicationContext);
218+
this.registrar.setApplicationContext(this.applicationContext);
218219

219-
if (registrar.getEndpointRegistry() == null) {
220-
if (endpointRegistry == null) {
221-
endpointRegistry = applicationContext
222-
.getBean(AnnotationConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME,
223-
JmsListenerEndpointRegistry.class);
220+
if (this.registrar.getEndpointRegistry() == null) {
221+
if (this.endpointRegistry == null) {
222+
this.endpointRegistry = this.applicationContext.getBean(
223+
AnnotationConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME, JmsListenerEndpointRegistry.class);
224224
}
225-
registrar.setEndpointRegistry(endpointRegistry);
225+
this.registrar.setEndpointRegistry(this.endpointRegistry);
226226
}
227227

228228
if (this.containerFactoryBeanName != null) {
229-
registrar.setContainerFactoryBeanName(this.containerFactoryBeanName);
229+
this.registrar.setContainerFactoryBeanName(this.containerFactoryBeanName);
230230
}
231231

232-
233232
// Set the custom handler method factory once resolved by the configurer
234233
JmsHandlerMethodFactory handlerMethodFactory = registrar.getJmsHandlerMethodFactory();
235234
if (handlerMethodFactory != null) {
@@ -238,10 +237,10 @@ public void onApplicationEvent(ContextRefreshedEvent event) {
238237

239238
// Create all the listeners and starts them
240239
try {
241-
registrar.afterPropertiesSet();
240+
this.registrar.afterPropertiesSet();
242241
}
243-
catch (Exception e) {
244-
throw new BeanInitializationException(e.getMessage(), e);
242+
catch (Exception ex) {
243+
throw new BeanInitializationException("Failed to initialize JmsListenerEndpointRegistrar", ex);
245244
}
246245
}
247246

@@ -250,11 +249,11 @@ private String getEndpointId(JmsListener jmsListener) {
250249
return jmsListener.id();
251250
}
252251
else {
253-
return "org.springframework.jms.JmsListenerEndpointContainer#"
254-
+ counter.getAndIncrement();
252+
return "org.springframework.jms.JmsListenerEndpointContainer#" + counter.getAndIncrement();
255253
}
256254
}
257255

256+
258257
/**
259258
* An {@link JmsHandlerMethodFactory} adapter that offers a configurable underlying
260259
* instance to use. Useful if the factory to use is determined once the endpoints
@@ -265,7 +264,7 @@ private class JmsHandlerMethodFactoryAdapter implements JmsHandlerMethodFactory
265264

266265
private JmsHandlerMethodFactory jmsHandlerMethodFactory;
267266

268-
private void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodFactory) {
267+
public void setJmsHandlerMethodFactory(JmsHandlerMethodFactory jmsHandlerMethodFactory) {
269268
this.jmsHandlerMethodFactory = jmsHandlerMethodFactory;
270269
}
271270

@@ -275,10 +274,10 @@ public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method m
275274
}
276275

277276
private JmsHandlerMethodFactory getJmsHandlerMethodFactory() {
278-
if (jmsHandlerMethodFactory == null) {
279-
jmsHandlerMethodFactory = createDefaultJmsHandlerMethodFactory();
277+
if (this.jmsHandlerMethodFactory == null) {
278+
this.jmsHandlerMethodFactory = createDefaultJmsHandlerMethodFactory();
280279
}
281-
return jmsHandlerMethodFactory;
280+
return this.jmsHandlerMethodFactory;
282281
}
283282

284283
private JmsHandlerMethodFactory createDefaultJmsHandlerMethodFactory() {

spring-jms/src/main/java/org/springframework/jms/config/AbstractJmsListenerContainerFactory.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.jms.config;
1818

19-
2019
import javax.jms.ConnectionFactory;
2120

2221
import org.apache.commons.logging.Log;
@@ -55,8 +54,13 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess
5554

5655
private Boolean subscriptionDurable;
5756

57+
private Boolean subscriptionShared;
58+
5859
private String clientId;
5960

61+
private Integer phase;
62+
63+
6064
/**
6165
* @see AbstractMessageListenerContainer#setConnectionFactory(ConnectionFactory)
6266
*/
@@ -113,6 +117,13 @@ public void setSubscriptionDurable(Boolean subscriptionDurable) {
113117
this.subscriptionDurable = subscriptionDurable;
114118
}
115119

120+
/**
121+
* @see AbstractMessageListenerContainer#setSubscriptionShared(boolean)
122+
*/
123+
public void setSubscriptionShared(Boolean subscriptionShared) {
124+
this.subscriptionShared = subscriptionShared;
125+
}
126+
116127
/**
117128
* @see AbstractMessageListenerContainer#setClientId(String)
118129
*/
@@ -121,12 +132,15 @@ public void setClientId(String clientId) {
121132
}
122133

123134
/**
124-
* Create an empty container instance.
135+
* @see AbstractMessageListenerContainer#setPhase(int)
125136
*/
126-
protected abstract C createContainerInstance();
137+
public void setPhase(int phase) {
138+
this.phase = phase;
139+
}
140+
127141

128142
@Override
129-
public C createMessageListenerContainer(JmsListenerEndpoint endpoint) {
143+
public C createListenerContainer(JmsListenerEndpoint endpoint) {
130144
C instance = createContainerInstance();
131145

132146
if (this.connectionFactory != null) {
@@ -141,38 +155,45 @@ public C createMessageListenerContainer(JmsListenerEndpoint endpoint) {
141155
if (this.messageConverter != null) {
142156
instance.setMessageConverter(this.messageConverter);
143157
}
144-
145158
if (this.sessionTransacted != null) {
146159
instance.setSessionTransacted(this.sessionTransacted);
147160
}
148161
if (this.sessionAcknowledgeMode != null) {
149162
instance.setSessionAcknowledgeMode(this.sessionAcknowledgeMode);
150163
}
151-
152164
if (this.pubSubDomain != null) {
153165
instance.setPubSubDomain(this.pubSubDomain);
154166
}
155167
if (this.subscriptionDurable != null) {
156168
instance.setSubscriptionDurable(this.subscriptionDurable);
157169
}
170+
if (this.subscriptionShared != null) {
171+
instance.setSubscriptionShared(this.subscriptionShared);
172+
}
158173
if (this.clientId != null) {
159174
instance.setClientId(this.clientId);
160175
}
176+
if (this.phase != null) {
177+
instance.setPhase(this.phase);
178+
}
161179

162-
endpoint.setupMessageContainer(instance);
163-
180+
endpoint.setupListenerContainer(instance);
164181
initializeContainer(instance);
165182

166183
return instance;
167184
}
168185

186+
/**
187+
* Create an empty container instance.
188+
*/
189+
protected abstract C createContainerInstance();
190+
169191
/**
170192
* Further initialize the specified container.
171193
* <p>Subclasses can inherit from this method to apply extra
172194
* configuration if necessary.
173195
*/
174196
protected void initializeContainer(C instance) {
175-
176197
}
177198

178199
}

0 commit comments

Comments
 (0)