Revise generated default name for @JmsListener subscription
The previous commit changed the generated default name for a JMS subscription to <FQCN>#<method name> -- for example: - org.example.MyListener#myListenerMethod However, the JMS spec does not guarantee that '#' is a supported character. This commit therefore changes '#' to '.' as the separator between the class name and method name -- for example: - org.example.MyListener.myListenerMethod This commit also introduces tests and documentation for these changes. See gh-29790
This commit is contained in:
@@ -79,6 +79,7 @@ import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
* <em>composed annotations</em> with attribute overrides.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see EnableJms
|
||||
* @see JmsListenerAnnotationBeanPostProcessor
|
||||
@@ -113,6 +114,12 @@ public @interface JmsListener {
|
||||
|
||||
/**
|
||||
* The name for the durable subscription, if any.
|
||||
* <p>As of Spring Framework 6.0.5, if an explicit subscription name is not
|
||||
* specified, a default subscription name will be generated based on the fully
|
||||
* qualified name of the annotated listener method — for example,
|
||||
* {@code "org.example.jms.ProductListener.processRequest"} for a
|
||||
* {@code processRequest(...)} listener method in the
|
||||
* {@code org.example.jms.ProductListener} class.
|
||||
*/
|
||||
String subscription() default "";
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ import org.springframework.util.Assert;
|
||||
* are provided as additional arguments so that these can be injected as
|
||||
* method arguments if necessary.
|
||||
*
|
||||
* <p>As of Spring Framework 6.0.5, {@code MessagingMessageListenerAdapter} implements
|
||||
* {@link SubscriptionNameProvider} in order to provide a meaningful default
|
||||
* subscription name. See {@link #getSubscriptionName()} for details.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
@@ -70,16 +74,6 @@ public class MessagingMessageListenerAdapter extends AbstractAdaptableMessageLis
|
||||
return this.handlerMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSubscriptionName() {
|
||||
if (this.handlerMethod != null) {
|
||||
return this.handlerMethod.getBeanType().getName() + "#" + this.handlerMethod.getMethod().getName();
|
||||
}
|
||||
else {
|
||||
return this.getClass().getName();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onMessage(jakarta.jms.Message jmsMessage, @Nullable Session session) throws JMSException {
|
||||
@@ -145,4 +139,28 @@ public class MessagingMessageListenerAdapter extends AbstractAdaptableMessageLis
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a subscription name for this {@code MessageListener} adapter based
|
||||
* on the following rules.
|
||||
* <ul>
|
||||
* <li>If the {@link #setHandlerMethod(InvocableHandlerMethod) handlerMethod}
|
||||
* has been set, the generated subscription name takes the form of
|
||||
* {@code handlerMethod.getBeanType().getName() + "." + handlerMethod.getMethod().getName()}.</li>
|
||||
* <li>Otherwise, the generated subscription name is the result of invoking
|
||||
* {@code getClass().getName()}, which aligns with the default behavior of
|
||||
* {@link org.springframework.jms.listener.AbstractMessageListenerContainer}.</li>
|
||||
* </ul>
|
||||
* @since 6.0.5
|
||||
* @see SubscriptionNameProvider#getSubscriptionName()
|
||||
*/
|
||||
@Override
|
||||
public String getSubscriptionName() {
|
||||
if (this.handlerMethod != null) {
|
||||
return this.handlerMethod.getBeanType().getName() + "." + this.handlerMethod.getMethod().getName();
|
||||
}
|
||||
else {
|
||||
return getClass().getName();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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
|
||||
*
|
||||
* https://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.listener.adapter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.jms.listener.SimpleMessageListenerContainer;
|
||||
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Named.named;
|
||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link MessagingMessageListenerAdapter}.
|
||||
*
|
||||
* <p>These tests are similar to those in {@link MessagingMessageListenerAdapterTests},
|
||||
* except that these tests have a different scope and do not use mocks.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.0.5
|
||||
* @see MessagingMessageListenerAdapterTests
|
||||
*/
|
||||
class MessagingMessageListenerAdapterIntegrationTests {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("subscriptionNames")
|
||||
void defaultSubscriptionName(Method method, String subscriptionName) {
|
||||
MessagingMessageListenerAdapter messageListenerAdaptor = new MessagingMessageListenerAdapter();
|
||||
InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(new CustomListener(), method);
|
||||
messageListenerAdaptor.setHandlerMethod(handlerMethod);
|
||||
|
||||
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
|
||||
assertThat(listenerContainer.getSubscriptionName()).isNull();
|
||||
|
||||
listenerContainer.setMessageListener(messageListenerAdaptor);
|
||||
assertThat(listenerContainer.getSubscriptionName()).isEqualTo(subscriptionName);
|
||||
}
|
||||
|
||||
|
||||
private static Stream<Arguments> subscriptionNames() {
|
||||
String method1 = "toUpperCase";
|
||||
String method2 = "toUpperCase(java.lang.String)";
|
||||
String method3 = "toUpperCase(java.lang.String,int)";
|
||||
String method4 = "toUpperCase(java.lang.String[])";
|
||||
String expectedName = CustomListener.class.getName() + ".toUpperCase";
|
||||
return Stream.of(
|
||||
arguments(named(method1, findMethod()), expectedName),
|
||||
arguments(named(method2, findMethod(String.class)), expectedName),
|
||||
arguments(named(method3, findMethod(String.class, String.class)), expectedName),
|
||||
arguments(named(method4, findMethod(byte[].class)), expectedName));
|
||||
}
|
||||
|
||||
private static Method findMethod(Class<?>... paramTypes) {
|
||||
return ReflectionUtils.findMethod(CustomListener.class, "toUpperCase", paramTypes);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class CustomListener {
|
||||
|
||||
// @JmsListener(...)
|
||||
String toUpperCase() {
|
||||
return "ENIGMA";
|
||||
}
|
||||
|
||||
// @JmsListener(...)
|
||||
String toUpperCase(String input) {
|
||||
return "ENIGMA";
|
||||
}
|
||||
|
||||
// @JmsListener(...)
|
||||
String toUpperCase(String input, String customHeader) {
|
||||
return "ENIGMA";
|
||||
}
|
||||
|
||||
// @JmsListener(...)
|
||||
String toUpperCase(byte[] input) {
|
||||
return "ENIGMA";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user