Support custom resolution of response destination

Previously, the "pubSubDomain" drove the resolution of both the
destination of the listener and the default response destination.

A new "replyPubSubDomain" attribute has been added on the base listener
and  can be used to listen on a topic and reply to a queue (or vice
versa). The attribute is exposed via the "response-destination-type" XML
attribute on the listener container element. It is also available on the
JmsListenerContainerFactory for use with the @JmsListener infrastructure.

Issue: SPR-12911
This commit is contained in:
Stephane Nicoll
2015-04-16 17:29:48 +02:00
parent e403aefe86
commit 2c7d2d38a9
16 changed files with 835 additions and 16 deletions

View File

@@ -52,6 +52,8 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess
private Boolean pubSubDomain;
private Boolean replyPubSubDomain;
private Boolean subscriptionDurable;
private Boolean subscriptionShared;
@@ -112,6 +114,13 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess
this.pubSubDomain = pubSubDomain;
}
/**
* @see AbstractMessageListenerContainer#setReplyPubSubDomain(boolean)
*/
public void setReplyPubSubDomain(Boolean replyPubSubDomain) {
this.replyPubSubDomain = replyPubSubDomain;
}
/**
* @see AbstractMessageListenerContainer#setSubscriptionDurable(boolean)
*/
@@ -172,6 +181,9 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess
if (this.pubSubDomain != null) {
instance.setPubSubDomain(this.pubSubDomain);
}
if (this.replyPubSubDomain != null) {
instance.setReplyPubSubDomain(this.replyPubSubDomain);
}
if (this.subscriptionDurable != null) {
instance.setSubscriptionDurable(this.subscriptionDurable);
}

View File

@@ -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.
@@ -78,6 +78,8 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
protected static final String DESTINATION_TYPE_SHARED_DURABLE_TOPIC = "sharedDurableTopic";
protected static final String RESPONSE_DESTINATION_TYPE_ATTRIBUTE = "response-destination-type";
protected static final String CLIENT_ID_ATTRIBUTE = "client-id";
protected static final String ACKNOWLEDGE_ATTRIBUTE = "acknowledge";
@@ -170,7 +172,7 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
if (listenerEle.hasAttribute(RESPONSE_DESTINATION_ATTRIBUTE)) {
String responseDestination = listenerEle.getAttribute(RESPONSE_DESTINATION_ATTRIBUTE);
Boolean pubSubDomain = (Boolean) commonContainerProperties.getPropertyValue("pubSubDomain").getValue();
Boolean pubSubDomain = (Boolean) commonContainerProperties.getPropertyValue("replyPubSubDomain").getValue();
listenerDef.getPropertyValues().add(
pubSubDomain ? "defaultResponseTopicName" : "defaultResponseQueueName", responseDestination);
if (containerDef.getPropertyValues().contains("destinationResolver")) {
@@ -260,6 +262,23 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
properties.add("subscriptionDurable", subscriptionDurable);
properties.add("subscriptionShared", subscriptionShared);
boolean replyPubSubDomain = false;
String replyDestinationType = containerEle.getAttribute(RESPONSE_DESTINATION_TYPE_ATTRIBUTE);
if (DESTINATION_TYPE_TOPIC.equals(replyDestinationType)) {
replyPubSubDomain = true;
}
else if (DESTINATION_TYPE_QUEUE.equals(replyDestinationType)) {
replyPubSubDomain = false;
}
else if (!StringUtils.hasText(replyDestinationType)) {
replyPubSubDomain = pubSubDomain; // the default: same value as pubSubDomain
}
else if (StringUtils.hasText(replyDestinationType)) {
parserContext.getReaderContext().error("Invalid listener container 'response-destination-type': only " +
"\"queue\", \"topic\" supported.", containerEle);
}
properties.add("replyPubSubDomain", replyPubSubDomain);
if (containerEle.hasAttribute(CLIENT_ID_ATTRIBUTE)) {
String clientId = containerEle.getAttribute(CLIENT_ID_ATTRIBUTE);
if (!StringUtils.hasText(clientId)) {

View File

@@ -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.
@@ -89,7 +89,7 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
messageListener.setHandlerMethod(invocableHandlerMethod);
String responseDestination = getDefaultResponseDestination();
if (StringUtils.hasText(responseDestination)) {
if (container.isPubSubDomain()) {
if (container.isReplyPubSubDomain()) {
messageListener.setDefaultResponseTopicName(responseDestination);
}
else {

View File

@@ -153,6 +153,8 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
private String subscriptionName;
private Boolean replyPubSubDomain;
private boolean pubSubNoLocal = false;
private MessageConverter messageConverter;
@@ -445,6 +447,34 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
return this.pubSubNoLocal;
}
/**
* Configure the reply destination type. By default, the configured {@code pubSubDomain}
* value is used (see {@link #isPubSubDomain()}.
* <p>This setting primarily indicates what type of destination to resolve
* if dynamic destinations are enabled.
* @param replyPubSubDomain "true" for the Publish/Subscribe domain ({@link javax.jms.Topic Topics}),
* "false" for the Point-to-Point domain ({@link javax.jms.Queue Queues})
* @see #setDestinationResolver
*/
public void setReplyPubSubDomain(boolean replyPubSubDomain) {
this.replyPubSubDomain = replyPubSubDomain;
}
/**
* Return whether the Publish/Subscribe domain ({@link javax.jms.Topic Topics}) is used
* for replies. Otherwise, the Point-to-Point domain ({@link javax.jms.Queue Queues}) is
* used.
*/
@Override
public boolean isReplyPubSubDomain() {
if (this.replyPubSubDomain != null) {
return replyPubSubDomain;
}
else {
return isPubSubDomain();
}
}
/**
* Set the {@link MessageConverter} strategy for converting JMS Messages.
* @since 4.1

View File

@@ -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.
@@ -47,4 +47,12 @@ public interface MessageListenerContainer extends SmartLifecycle {
*/
boolean isPubSubDomain();
/**
* Return whether the reply destination uses Publish/Subscribe domain
* ({@link javax.jms.Topic Topics}). Otherwise, the Point-to-Point domain
* ({@link javax.jms.Queue Queues}) is used.
* <p>By default, the value is identical to {@link #isPubSubDomain()}.
*/
boolean isReplyPubSubDomain();
}

View File

@@ -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.
@@ -46,6 +46,8 @@ public class JmsActivationSpecConfig {
private boolean pubSubDomain = false;
private Boolean replyPubSubDomain;
private boolean subscriptionDurable = false;
private boolean subscriptionShared = false;
@@ -81,6 +83,19 @@ public class JmsActivationSpecConfig {
return this.pubSubDomain;
}
public void setReplyPubSubDomain(boolean replyPubSubDomain) {
this.replyPubSubDomain = replyPubSubDomain;
}
public boolean isReplyPubSubDomain() {
if (this.replyPubSubDomain != null) {
return this.replyPubSubDomain;
}
else {
return isPubSubDomain();
}
}
public void setSubscriptionDurable(boolean subscriptionDurable) {
this.subscriptionDurable = subscriptionDurable;
if (subscriptionDurable) {

View File

@@ -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.
@@ -200,4 +200,13 @@ public class JmsMessageEndpointManager extends GenericMessageEndpointManager
throw new IllegalStateException("Could not determine pubSubDomain - no activation spec config is set");
}
@Override
public boolean isReplyPubSubDomain() {
JmsActivationSpecConfig config = getActivationSpecConfig();
if (config != null) {
return config.isReplyPubSubDomain();
}
throw new IllegalStateException("Could not determine reply pubSubDomain - no activation spec config is set");
}
}