diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java index 4a9cf987b4..29cfaa10f7 100644 --- a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java @@ -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; 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; * @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 { /** diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java index ce245f609d..373f8443a4 100644 --- a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java @@ -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); } diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListeners.java b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListeners.java new file mode 100644 index 0000000000..f3d5266e52 --- /dev/null +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListeners.java @@ -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. + * + *
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();
+
+}
diff --git a/spring-jms/src/test/java/org/springframework/jms/annotation/AbstractJmsAnnotationDrivenTests.java b/spring-jms/src/test/java/org/springframework/jms/annotation/AbstractJmsAnnotationDrivenTests.java
index 612f8693bd..b58bfb4dd6 100644
--- a/spring-jms/src/test/java/org/springframework/jms/annotation/AbstractJmsAnnotationDrivenTests.java
+++ b/spring-jms/src/test/java/org/springframework/jms/annotation/AbstractJmsAnnotationDrivenTests.java
@@ -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 {
}
}
+ /**
+ * 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
diff --git a/spring-jms/src/test/java/org/springframework/jms/annotation/AnnotationDrivenNamespaceTests.java b/spring-jms/src/test/java/org/springframework/jms/annotation/AnnotationDrivenNamespaceTests.java
index 22f8a334ea..74f7b7844b 100644
--- a/spring-jms/src/test/java/org/springframework/jms/annotation/AnnotationDrivenNamespaceTests.java
+++ b/spring-jms/src/test/java/org/springframework/jms/annotation/AnnotationDrivenNamespaceTests.java
@@ -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
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;
diff --git a/spring-jms/src/test/java/org/springframework/jms/annotation/EnableJmsTests.java b/spring-jms/src/test/java/org/springframework/jms/annotation/EnableJmsTests.java
index e4a2613d01..45d2877df8 100644
--- a/spring-jms/src/test/java/org/springframework/jms/annotation/EnableJmsTests.java
+++ b/spring-jms/src/test/java/org/springframework/jms/annotation/EnableJmsTests.java
@@ -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);
diff --git a/spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerTestFactory.java b/spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerTestFactory.java
index f67d976fcf..d51e06ee63 100644
--- a/spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerTestFactory.java
+++ b/spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerTestFactory.java
@@ -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 @@
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