Introduce SimpleDestinationResolver as new default for common setups
Closes gh-35456
This commit is contained in:
parent
c5a79fdf99
commit
1333669e2c
|
@ -86,6 +86,8 @@ public interface JmsClient {
|
|||
* Provide an operation handle for the given JMS destination.
|
||||
* @param destination the JMS {@code Destination} object
|
||||
* @return a reusable operation handle bound to the destination
|
||||
* @see jakarta.jms.Queue
|
||||
* @see jakarta.jms.Topic
|
||||
*/
|
||||
OperationSpec destination(Destination destination);
|
||||
|
||||
|
@ -93,7 +95,8 @@ public interface JmsClient {
|
|||
* Provide an operation handle for the specified JMS destination.
|
||||
* @param destinationName a name resolving to a JMS {@code Destination}
|
||||
* @return a reusable operation handle bound to the destination
|
||||
* @see org.springframework.jms.support.destination.DestinationResolver
|
||||
* @see org.springframework.jms.support.destination.SimpleDestinationResolver
|
||||
* @see JmsTemplate#setDestinationResolver
|
||||
*/
|
||||
OperationSpec destination(String destinationName);
|
||||
|
||||
|
@ -138,6 +141,7 @@ public interface JmsClient {
|
|||
return new DefaultJmsClientBuilder(jmsTemplate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A mutable builder for creating a {@link JmsClient}.
|
||||
*/
|
||||
|
@ -163,7 +167,6 @@ public interface JmsClient {
|
|||
* Build the {@code JmsClient} instance.
|
||||
*/
|
||||
JmsClient build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ import org.springframework.util.ClassUtils;
|
|||
* the "sessionTransacted" and "sessionAcknowledgeMode" bean properties.
|
||||
*
|
||||
* <p>This template uses a
|
||||
* {@link org.springframework.jms.support.destination.DynamicDestinationResolver}
|
||||
* {@link org.springframework.jms.support.destination.SimpleDestinationResolver}
|
||||
* and a {@link org.springframework.jms.support.converter.SimpleMessageConverter}
|
||||
* as default strategies for resolving a destination name or converting a message,
|
||||
* respectively. These defaults can be overridden through the "destinationResolver"
|
||||
|
|
|
@ -39,7 +39,7 @@ import org.springframework.jms.support.converter.MessagingMessageConverter;
|
|||
import org.springframework.jms.support.converter.SimpleMessageConverter;
|
||||
import org.springframework.jms.support.converter.SmartMessageConverter;
|
||||
import org.springframework.jms.support.destination.DestinationResolver;
|
||||
import org.springframework.jms.support.destination.DynamicDestinationResolver;
|
||||
import org.springframework.jms.support.destination.SimpleDestinationResolver;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -61,7 +61,7 @@ public abstract class AbstractAdaptableMessageListener
|
|||
|
||||
private @Nullable Object defaultResponseDestination;
|
||||
|
||||
private DestinationResolver destinationResolver = new DynamicDestinationResolver();
|
||||
private DestinationResolver destinationResolver = new SimpleDestinationResolver();
|
||||
|
||||
private @Nullable MessageConverter messageConverter = new SimpleMessageConverter();
|
||||
|
||||
|
@ -113,9 +113,9 @@ public abstract class AbstractAdaptableMessageListener
|
|||
/**
|
||||
* Set the DestinationResolver that should be used to resolve response
|
||||
* destination names for this adapter.
|
||||
* <p>The default resolver is a DynamicDestinationResolver. Specify a
|
||||
* <p>The default resolver is a SimpleDestinationResolver. Specify a
|
||||
* JndiDestinationResolver for resolving destination names as JNDI locations.
|
||||
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
|
||||
* @see org.springframework.jms.support.destination.SimpleDestinationResolver
|
||||
* @see org.springframework.jms.support.destination.JndiDestinationResolver
|
||||
*/
|
||||
public void setDestinationResolver(DestinationResolver destinationResolver) {
|
||||
|
|
|
@ -86,7 +86,8 @@ public class StandardJmsActivationSpecFactory implements JmsActivationSpecFactor
|
|||
* able to work <i>without</i> an active JMS Session: for example,
|
||||
* {@link org.springframework.jms.support.destination.JndiDestinationResolver}
|
||||
* or {@link org.springframework.jms.support.destination.BeanFactoryDestinationResolver}
|
||||
* but not {@link org.springframework.jms.support.destination.DynamicDestinationResolver}.
|
||||
* but not {@link org.springframework.jms.support.destination.SimpleDestinationResolver}
|
||||
* or {@link org.springframework.jms.support.destination.DynamicDestinationResolver}.
|
||||
*/
|
||||
public void setDestinationResolver(@Nullable DestinationResolver destinationResolver) {
|
||||
this.destinationResolver = destinationResolver;
|
||||
|
|
|
@ -30,13 +30,13 @@ import org.jspecify.annotations.Nullable;
|
|||
*
|
||||
* <p>The default {@link DestinationResolver} implementation used by
|
||||
* {@link org.springframework.jms.core.JmsTemplate} instances is the
|
||||
* {@link DynamicDestinationResolver} class. Consider using the
|
||||
* {@link SimpleDestinationResolver} class. Consider using the
|
||||
* {@link JndiDestinationResolver} for more advanced scenarios.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see org.springframework.jms.core.JmsTemplate#setDestinationResolver
|
||||
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
|
||||
* @see org.springframework.jms.support.destination.SimpleDestinationResolver
|
||||
* @see org.springframework.jms.support.destination.JndiDestinationResolver
|
||||
*/
|
||||
@FunctionalInterface
|
||||
|
|
|
@ -26,11 +26,12 @@ import org.jspecify.annotations.Nullable;
|
|||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple {@link DestinationResolver} implementation resolving destination names
|
||||
* as dynamic destinations.
|
||||
* A basic {@link DestinationResolver} implementation freshly resolving
|
||||
* destination names as dynamic destinations against a given {@link Session}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see SimpleDestinationResolver
|
||||
* @see jakarta.jms.Session#createQueue
|
||||
* @see jakarta.jms.Session#createTopic
|
||||
*/
|
||||
|
|
|
@ -55,7 +55,7 @@ public abstract class JmsDestinationAccessor extends JmsAccessor {
|
|||
public static final long RECEIVE_TIMEOUT_INDEFINITE_WAIT = 0;
|
||||
|
||||
|
||||
private DestinationResolver destinationResolver = new DynamicDestinationResolver();
|
||||
private DestinationResolver destinationResolver = new SimpleDestinationResolver();
|
||||
|
||||
private boolean pubSubDomain = false;
|
||||
|
||||
|
@ -63,9 +63,9 @@ public abstract class JmsDestinationAccessor extends JmsAccessor {
|
|||
/**
|
||||
* Set the {@link DestinationResolver} that is to be used to resolve
|
||||
* {@link jakarta.jms.Destination} references for this accessor.
|
||||
* <p>The default resolver is a DynamicDestinationResolver. Specify a
|
||||
* <p>The default resolver is a SimpleDestinationResolver. Specify a
|
||||
* JndiDestinationResolver for resolving destination names as JNDI locations.
|
||||
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
|
||||
* @see org.springframework.jms.support.destination.SimpleDestinationResolver
|
||||
* @see org.springframework.jms.support.destination.JndiDestinationResolver
|
||||
*/
|
||||
public void setDestinationResolver(DestinationResolver destinationResolver) {
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2002-present 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.support.destination;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import jakarta.jms.JMSException;
|
||||
import jakarta.jms.Queue;
|
||||
import jakarta.jms.Session;
|
||||
import jakarta.jms.Topic;
|
||||
|
||||
/**
|
||||
* A simple {@link DestinationResolver} implementation for {@link Session}-based
|
||||
* destination resolution, caching {@link Queue} and {@link Topic} instances per
|
||||
* queue/topic name. In that sense, the destinations themselves also need to be
|
||||
* "simple": not Session-specific and therefore stable across an entire JMS setup.
|
||||
*
|
||||
* <p>This is the default resolver used by {@link org.springframework.jms.core.JmsClient}
|
||||
* and also {@link org.springframework.jms.core.JmsTemplate} and listener containers,
|
||||
* as of 7.0. For enforcing fresh resolution on every call, you may explicitly set a
|
||||
* {@link DynamicDestinationResolver} instead.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 7.0
|
||||
* @see DynamicDestinationResolver
|
||||
* @see jakarta.jms.Session#createQueue
|
||||
* @see jakarta.jms.Session#createTopic
|
||||
*/
|
||||
public class SimpleDestinationResolver extends DynamicDestinationResolver implements CachingDestinationResolver {
|
||||
|
||||
private final Map<String, Topic> topicCache = new ConcurrentHashMap<>(4);
|
||||
|
||||
private final Map<String, Queue> queueCache = new ConcurrentHashMap<>(4);
|
||||
|
||||
|
||||
@Override
|
||||
protected Topic resolveTopic(Session session, String topicName) throws JMSException {
|
||||
Topic topic = this.topicCache.get(topicName);
|
||||
if (topic != null) {
|
||||
return topic;
|
||||
}
|
||||
topic = super.resolveTopic(session, topicName);
|
||||
Topic existing = this.topicCache.putIfAbsent(topicName, topic);
|
||||
return (existing != null ? existing : topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Queue resolveQueue(Session session, String queueName) throws JMSException {
|
||||
Queue queue = this.queueCache.get(queueName);
|
||||
if (queue != null) {
|
||||
return queue;
|
||||
}
|
||||
queue = super.resolveQueue(session, queueName);
|
||||
Queue existing = this.queueCache.putIfAbsent(queueName, queue);
|
||||
return (existing != null ? existing : queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFromCache(String destinationName) {
|
||||
this.topicCache.remove(destinationName);
|
||||
this.queueCache.remove(destinationName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache() {
|
||||
this.topicCache.clear();
|
||||
this.queueCache.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -34,49 +34,67 @@ import static org.mockito.Mockito.mock;
|
|||
|
||||
/**
|
||||
* @author Rick Evans
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class DynamicDestinationResolverTests {
|
||||
|
||||
private static final String DESTINATION_NAME = "foo";
|
||||
|
||||
private final DynamicDestinationResolver resolver = new DynamicDestinationResolver();
|
||||
|
||||
|
||||
@Test
|
||||
void resolveWithPubSubTopicSession() throws Exception {
|
||||
Topic expectedDestination = new StubTopic();
|
||||
Topic expectedDestination1 = new StubTopic();
|
||||
Topic expectedDestination2 = new StubTopic();
|
||||
TopicSession session = mock();
|
||||
given(session.createTopic(DESTINATION_NAME)).willReturn(expectedDestination);
|
||||
testResolveDestination(session, expectedDestination, true);
|
||||
|
||||
given(session.createTopic(DESTINATION_NAME)).willReturn(expectedDestination1);
|
||||
testResolveDestination(session, expectedDestination1, true);
|
||||
given(session.createTopic(DESTINATION_NAME)).willReturn(expectedDestination2);
|
||||
testResolveDestination(session, expectedDestination2, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWithPubSubVanillaSession() throws Exception {
|
||||
Topic expectedDestination = new StubTopic();
|
||||
Topic expectedDestination1 = new StubTopic();
|
||||
Topic expectedDestination2 = new StubTopic();
|
||||
Session session = mock();
|
||||
given(session.createTopic(DESTINATION_NAME)).willReturn(expectedDestination);
|
||||
testResolveDestination(session, expectedDestination, true);
|
||||
|
||||
given(session.createTopic(DESTINATION_NAME)).willReturn(expectedDestination1);
|
||||
testResolveDestination(session, expectedDestination1, true);
|
||||
given(session.createTopic(DESTINATION_NAME)).willReturn(expectedDestination2);
|
||||
testResolveDestination(session, expectedDestination2, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWithPointToPointQueueSession() throws Exception {
|
||||
Queue expectedDestination = new StubQueue();
|
||||
Queue expectedDestination1 = new StubQueue();
|
||||
Queue expectedDestination2 = new StubQueue();
|
||||
QueueSession session = mock();
|
||||
given(session.createQueue(DESTINATION_NAME)).willReturn(expectedDestination);
|
||||
testResolveDestination(session, expectedDestination, false);
|
||||
|
||||
given(session.createQueue(DESTINATION_NAME)).willReturn(expectedDestination1);
|
||||
testResolveDestination(session, expectedDestination1, false);
|
||||
given(session.createQueue(DESTINATION_NAME)).willReturn(expectedDestination2);
|
||||
testResolveDestination(session, expectedDestination2, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWithPointToPointVanillaSession() throws Exception {
|
||||
Queue expectedDestination = new StubQueue();
|
||||
Queue expectedDestination1 = new StubQueue();
|
||||
Queue expectedDestination2 = new StubQueue();
|
||||
Session session = mock();
|
||||
given(session.createQueue(DESTINATION_NAME)).willReturn(expectedDestination);
|
||||
testResolveDestination(session, expectedDestination, false);
|
||||
|
||||
given(session.createQueue(DESTINATION_NAME)).willReturn(expectedDestination1);
|
||||
testResolveDestination(session, expectedDestination1, false);
|
||||
given(session.createQueue(DESTINATION_NAME)).willReturn(expectedDestination2);
|
||||
testResolveDestination(session, expectedDestination2, false);
|
||||
}
|
||||
|
||||
private static void testResolveDestination(Session session, Destination expectedDestination, boolean isPubSub) throws JMSException {
|
||||
DynamicDestinationResolver resolver = new DynamicDestinationResolver();
|
||||
private void testResolveDestination(Session session, Destination expected, boolean isPubSub) throws JMSException {
|
||||
Destination destination = resolver.resolveDestinationName(session, DESTINATION_NAME, isPubSub);
|
||||
assertThat(destination).isNotNull();
|
||||
assertThat(destination).isSameAs(expectedDestination);
|
||||
assertThat(destination).isSameAs(expected);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright 2002-present 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.support.destination;
|
||||
|
||||
import jakarta.jms.Destination;
|
||||
import jakarta.jms.JMSException;
|
||||
import jakarta.jms.Queue;
|
||||
import jakarta.jms.QueueSession;
|
||||
import jakarta.jms.Session;
|
||||
import jakarta.jms.Topic;
|
||||
import jakarta.jms.TopicSession;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.jms.StubQueue;
|
||||
import org.springframework.jms.StubTopic;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @since 7.0
|
||||
*/
|
||||
class SimpleDestinationResolverTests {
|
||||
|
||||
private static final String DESTINATION_NAME = "foo";
|
||||
|
||||
private final SimpleDestinationResolver resolver = new SimpleDestinationResolver();
|
||||
|
||||
|
||||
@Test
|
||||
void resolveWithPubSubTopicSession() throws Exception {
|
||||
Topic expectedDestination1 = new StubTopic();
|
||||
Topic expectedDestination2 = new StubTopic();
|
||||
TopicSession session = mock();
|
||||
|
||||
given(session.createTopic(DESTINATION_NAME)).willReturn(expectedDestination1);
|
||||
testResolveDestination(session, expectedDestination1, true);
|
||||
given(session.createTopic(DESTINATION_NAME)).willReturn(expectedDestination2);
|
||||
testResolveDestination(session, expectedDestination1, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWithPubSubVanillaSession() throws Exception {
|
||||
Topic expectedDestination1 = new StubTopic();
|
||||
Topic expectedDestination2 = new StubTopic();
|
||||
Session session = mock();
|
||||
|
||||
given(session.createTopic(DESTINATION_NAME)).willReturn(expectedDestination1);
|
||||
testResolveDestination(session, expectedDestination1, true);
|
||||
given(session.createTopic(DESTINATION_NAME)).willReturn(expectedDestination2);
|
||||
testResolveDestination(session, expectedDestination1, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWithPointToPointQueueSession() throws Exception {
|
||||
Queue expectedDestination1 = new StubQueue();
|
||||
Queue expectedDestination2 = new StubQueue();
|
||||
QueueSession session = mock();
|
||||
|
||||
given(session.createQueue(DESTINATION_NAME)).willReturn(expectedDestination1);
|
||||
testResolveDestination(session, expectedDestination1, false);
|
||||
given(session.createQueue(DESTINATION_NAME)).willReturn(expectedDestination2);
|
||||
testResolveDestination(session, expectedDestination1, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWithPointToPointVanillaSession() throws Exception {
|
||||
Queue expectedDestination1 = new StubQueue();
|
||||
Queue expectedDestination2 = new StubQueue();
|
||||
Session session = mock();
|
||||
|
||||
given(session.createQueue(DESTINATION_NAME)).willReturn(expectedDestination1);
|
||||
testResolveDestination(session, expectedDestination1, false);
|
||||
given(session.createQueue(DESTINATION_NAME)).willReturn(expectedDestination2);
|
||||
testResolveDestination(session, expectedDestination1, false);
|
||||
}
|
||||
|
||||
private void testResolveDestination(Session session, Destination expected, boolean isPubSub) throws JMSException {
|
||||
Destination destination = resolver.resolveDestinationName(session, DESTINATION_NAME, isPubSub);
|
||||
assertThat(destination).isNotNull();
|
||||
assertThat(destination).isSameAs(expected);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue