Browse Source

Add support for repeatable JmsListener

Previously, a method could only declare one Jms endpoint so if several
destinations share the exact same business logic, you'd still need one
separate method declaration per destination.

We now make sure that JmsListener is a repeatable annotation, introducing
JmsListeners for pre Java8 use cases.

Issue: SPR-13147
pull/825/head
Stephane Nicoll 10 years ago
parent
commit
4631add6cf
  1. 5
      spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java
  2. 4
      spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java
  3. 44
      spring-jms/src/main/java/org/springframework/jms/annotation/JmsListeners.java
  4. 50
      spring-jms/src/test/java/org/springframework/jms/annotation/AbstractJmsAnnotationDrivenTests.java
  5. 16
      spring-jms/src/test/java/org/springframework/jms/annotation/AnnotationDrivenNamespaceTests.java
  6. 14
      spring-jms/src/test/java/org/springframework/jms/annotation/EnableJmsTests.java
  7. 17
      spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerTestFactory.java
  8. 18
      spring-jms/src/test/resources/org/springframework/jms/annotation/annotation-driven-jms-listener-repeatable.xml
  9. 18
      spring-jms/src/test/resources/org/springframework/jms/annotation/annotation-driven-jms-listeners.xml
  10. 4
      src/asciidoc/integration.adoc

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.jms.annotation; @@ -18,6 +18,7 @@ package org.springframework.jms.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@ -70,11 +71,13 @@ import org.springframework.messaging.handler.annotation.MessageMapping; @@ -70,11 +71,13 @@ import org.springframework.messaging.handler.annotation.MessageMapping;
* @since 4.1
* @see EnableJms
* @see JmsListenerAnnotationBeanPostProcessor
* @see JmsListeners
*/
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@MessageMapping
@Documented
@Repeatable(JmsListeners.class)
public @interface JmsListener {
/**

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

@ -199,8 +199,8 @@ public class JmsListenerAnnotationBeanPostProcessor @@ -199,8 +199,8 @@ public class JmsListenerAnnotationBeanPostProcessor
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
JmsListener jmsListener = AnnotationUtils.getAnnotation(method, JmsListener.class);
if (jmsListener != null) {
for (JmsListener jmsListener :
AnnotationUtils.getRepeatableAnnotations(method, JmsListener.class, JmsListeners.class)) {
processJmsListener(jmsListener, method, bean);
annotatedMethods.add(method);
}

44
spring-jms/src/main/java/org/springframework/jms/annotation/JmsListeners.java

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jms.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Container annotation that aggregates several {@link JmsListener} annotations.
*
* <p>Can be used natively, declaring several nested {@link JmsListener} annotations.
* Can also be used in conjunction with Java 8's support for repeatable annotations,
* where {@link JmsListener} can simply be declared several times on the same method,
* implicitly generating this container annotation.
*
* @author Stephane Nicoll
* @since 4.2
* @see JmsListener
*/
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JmsListeners {
JmsListener[] value();
}

50
spring-jms/src/test/java/org/springframework/jms/annotation/AbstractJmsAnnotationDrivenTests.java

@ -73,6 +73,12 @@ public abstract class AbstractJmsAnnotationDrivenTests { @@ -73,6 +73,12 @@ public abstract class AbstractJmsAnnotationDrivenTests {
@Test
public abstract void jmsHandlerMethodFactoryConfiguration() throws JMSException;
@Test
public abstract void jmsListenerIsRepeatable();
@Test
public abstract void jmsListeners();
/**
* Test for {@link SampleBean} discovery. If a factory with the default name
* is set, an endpoint will use it automatically
@ -234,6 +240,50 @@ public abstract class AbstractJmsAnnotationDrivenTests { @@ -234,6 +240,50 @@ public abstract class AbstractJmsAnnotationDrivenTests {
}
}
/**
* Test for {@link JmsListenerRepeatableBean} and {@link JmsListenersBean} that validates that the
* {@code @JmsListener} annotation is repeatable and generate one specific container per annotation.
*/
public void testJmsListenerRepeatable(ApplicationContext context) {
JmsListenerContainerTestFactory simpleFactory =
context.getBean("jmsListenerContainerFactory", JmsListenerContainerTestFactory.class);
assertEquals(2, simpleFactory.getListenerContainers().size());
MethodJmsListenerEndpoint first = (MethodJmsListenerEndpoint)
simpleFactory.getListenerContainer("first").getEndpoint();
assertEquals("first", first.getId());
assertEquals("myQueue", first.getDestination());
assertEquals(null, first.getConcurrency());
MethodJmsListenerEndpoint second = (MethodJmsListenerEndpoint)
simpleFactory.getListenerContainer("second").getEndpoint();
assertEquals("second", second.getId());
assertEquals("anotherQueue", second.getDestination());
assertEquals("2-10", second.getConcurrency());
}
@Component
static class JmsListenerRepeatableBean {
@JmsListener(id = "first", destination = "myQueue")
@JmsListener(id = "second", destination = "anotherQueue", concurrency = "2-10")
public void repeatableHandle(String msg) {
}
}
@Component
static class JmsListenersBean {
@JmsListeners({
@JmsListener(id = "first", destination = "myQueue"),
@JmsListener(id = "second", destination = "anotherQueue", concurrency = "2-10")
})
public void repeatableHandle(String msg) {
}
}
static class TestValidator implements Validator {
@Override

16
spring-jms/src/test/java/org/springframework/jms/annotation/AnnotationDrivenNamespaceTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -91,6 +91,20 @@ public class AnnotationDrivenNamespaceTests extends AbstractJmsAnnotationDrivenT @@ -91,6 +91,20 @@ public class AnnotationDrivenNamespaceTests extends AbstractJmsAnnotationDrivenT
testJmsHandlerMethodFactoryConfiguration(context);
}
@Override
public void jmsListenerIsRepeatable() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"annotation-driven-jms-listener-repeatable.xml", getClass());
testJmsListenerRepeatable(context);
}
@Override
public void jmsListeners() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"annotation-driven-jms-listeners.xml", getClass());
testJmsListenerRepeatable(context);
}
static class CustomJmsListenerConfigurer implements JmsListenerConfigurer {
private MessageListener messageListener;

14
spring-jms/src/test/java/org/springframework/jms/annotation/EnableJmsTests.java

@ -112,6 +112,20 @@ public class EnableJmsTests extends AbstractJmsAnnotationDrivenTests { @@ -112,6 +112,20 @@ public class EnableJmsTests extends AbstractJmsAnnotationDrivenTests {
testJmsHandlerMethodFactoryConfiguration(context);
}
@Override
public void jmsListenerIsRepeatable() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
EnableJmsDefaultContainerFactoryConfig.class, JmsListenerRepeatableBean.class);
testJmsListenerRepeatable(context);
}
@Override
public void jmsListeners() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
EnableJmsDefaultContainerFactoryConfig.class, JmsListenersBean.class);
testJmsListenerRepeatable(context);
}
@Test
public void unknownFactory() {
thrown.expect(BeanCreationException.class);

17
spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerTestFactory.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,25 +17,30 @@ @@ -17,25 +17,30 @@
package org.springframework.jms.config;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author Stephane Nicoll
*/
public class JmsListenerContainerTestFactory implements JmsListenerContainerFactory<MessageListenerTestContainer> {
private final List<MessageListenerTestContainer> listenerContainers =
new ArrayList<MessageListenerTestContainer>();
private final Map<String, MessageListenerTestContainer> listenerContainers =
new LinkedHashMap<>();
public List<MessageListenerTestContainer> getListenerContainers() {
return listenerContainers;
return new ArrayList<>(this.listenerContainers.values());
}
public MessageListenerTestContainer getListenerContainer(String id) {
return this.listenerContainers.get(id);
}
@Override
public MessageListenerTestContainer createListenerContainer(JmsListenerEndpoint endpoint) {
MessageListenerTestContainer container = new MessageListenerTestContainer(endpoint);
this.listenerContainers.add(container);
this.listenerContainers.put(endpoint.getId(), container);
return container;
}

18
spring-jms/src/test/resources/org/springframework/jms/annotation/annotation-driven-jms-listener-repeatable.xml

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms-4.1.xsd">
<jms:annotation-driven/>
<bean class="org.springframework.jms.annotation.AbstractJmsAnnotationDrivenTests$JmsListenerRepeatableBean"/>
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.JmsListenerContainerTestFactory"/>
</beans>

18
spring-jms/src/test/resources/org/springframework/jms/annotation/annotation-driven-jms-listeners.xml

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms-4.1.xsd">
<jms:annotation-driven/>
<bean class="org.springframework.jms.annotation.AbstractJmsAnnotationDrivenTests$JmsListenersBean"/>
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.JmsListenerContainerTestFactory"/>
</beans>

4
src/asciidoc/integration.adoc

@ -2577,6 +2577,10 @@ provides). @@ -2577,6 +2577,10 @@ provides).
The annotated endpoint infrastructure creates a message listener container
behind the scenes for each annotated method, using a `JmsListenerContainerFactory`.
TIP: `@JmsListener` is a _repeatable_ annotation so it is possible to associate several
JMS destinations to the same method by adding additional `@JmsListener` declaration on
it. For pre Java8 use cases, you can use `@JmsListeners`.
[[jms-annotated-support]]
==== Enable listener endpoint annotations

Loading…
Cancel
Save